2017年7月
            1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31          

Amazonウィジェット

  • miniPC
  • 最近買った本
  • Raspberry Pi
  • クアッドコプター
  • 書籍ランキング

AdSense

  • 広告
無料ブログはココログ

« 2016年5月 | トップページ | 2016年11月 »

2016年10月

2016年10月30日 (日)

Python lsコマンドからファイルのリストを受け取るやりかた

ファイルのリストを受け取ってpythonで処理しようとするときに、lsでファイルのリストを一旦ファイルに作ってから、それを読ます方法がありますが、ちょっと面倒なので直接やる方法を考えていました。

subprocessというのを使うと、外部のコマンドやプログラムを走らせることができる。

しかしlsからの結果を受け取ろうとして、
a = subprocess.call("ls -l")
とやってしまうと何も受け取れない。

これをやるくらいならpythonで用意されている、
import glob
files = glob.glob("*.JPG")

を使ったほうが速いし覚えやすい。
globのimportのやり方を間違うと少し嵌りそう。

次はパイプを使ってやる方法。


from subprocess import *

output = Popen( ["ls","-l"],stdout=PIPE)

while True:
    line = output.stdout.readline()
    if not line:
        break
    print line,

ちなみに、"ls -1"とすれば、ファイル名のリストが一行ずつで出力される。

インタラクティブシェルでいろいろ試せば見えてくる。

from subprocess import *
でとりあえずインポートしたら、

output = Popen(["ls","-l"],stdout=PIPE)
をつかって出力を受け取る。
ただし、ここまでだとstdoutとstderrが混ざったオブジェクトの状態なので、
stdoutだけを取り出すようにする。

output.stdout.read()
を使えば、すべての中身を読みだせる。
これを何かで受け取って、

a = output.stdout.read()
としたあと、
lines = a.split('\n')
とすれば、
簡単に行ごとの中身が手に入る。

ほかにも、aからそのままの状態で、
a.count(".py")
とすれば、この単語が何個あるか数えられたり、

len(lines)
とすれば何行出力されたかわかる。ただしls -lの一行目は、ファイル名ではない。




2016年10月23日 (日)

C言語 関数ポインタを使って遊ぶ

可変長引数を使うと、いくらでも引数を渡せるということがわかったので、それを使って関数ポインタを渡したらどうなるかやってみました。まずは関数ポインタというのがどんな風に使われるかの基本をおさらいしてから、この記事の本題に移っていきます。




まず、九九の表を作れといわれたとする。これから何が起こるのか知らされもせず。

#include 

int mult(int a, int b){        return a*b; } void dispCalc(void){        for (int i = 0; i < 10; i++){               for (int j = 0; j < 10; j++){                      int res = mult(i, j);                      printf("%3d,", res);               }               puts("");        } } void main(void){        dispCalc();        getchar(); }

じゃあ足し算の表は?となったとき、mult関数とは別にdispCalcAddでも作ればよいのだが、 もっと良い方法に、計算方法を書いた関数へのポインタを渡すというのがある。

#include 

int mult(int a, int b){        return a*b; } int add(int a, int b){        return a+b; } void dispCalc(int (*func)(int,int)){        for (int i = 0; i < 10; i++){               for (int j = 0; j < 10; j++){                      int res = func(i, j);                      printf("%3d,", res);               }               puts("");        } } void main(void){        dispCalc(mult);        dispCalc(add);        getchar(); }


ここまでが関数ポインタの概要。同じ機能を実現する関数は、振る舞いに応じて別々に関数を作らなくても、どのような振る舞いをするかを関数ポインタで渡してやればよいということ。

ここからが本題。じゃあ九九の表と足し算の表のあと、もう一回九九の表を書け。とか、ファイルに何の表をどの順番で書くか指示が書いてあるからその順番で書けとか、もうむちゃくちゃ色々な指示が出たらどうするか。そこで可変長引数を使ってみたら、

#include 

#include

//計算をする関数のポインタをtypedefする typedef int(*calc)(int, int); int mult(int a, int b){        return a*b; } int add(int a, int b){        return a + b; } void execList(int n,int a,int b, ...){        va_list argv;        va_start(argv, b);        for (int i = 0; i < n; i++){               calc pnt = va_arg(argv, calc);               int result = (pnt)(a, b);               printf("res %d\n", result);        }        va_end(argv); } void main(void){        //掛け算、足し算、掛け算        execList(3, 5, 2, mult, add, mult);        getchar(); }


関数のポインタをtypedefしておくと少しでも見やすくなる。また型チェックで警告も出してもらえるかもしれない。


ゲームのキャラクターの振る舞いをセットしたり、いろんなログを決まった順番で加工したり。色々使えそうだ。

2016年10月16日 (日)

C言語 sscanfの続きとvsscanfとの違い。それぞれの使いどころ

前回の記事でsscanfを使って文字列の読み込みをやりました。今日はもう少しsscanfを使ったテクニックを掘り下げます。

scanf、sscanfのほかに、vsscanfというのがあるのですが、これは可変長引数が受け取れるようになっています。可変長引数というのは、引数を何個でも受け取れるということ。しかしどこで使えばいいのか。一見sscanfとの違いがわからずもやもやしてしまいました。

以下、vsscanfを使ったときのメモです。

下記のような3種類のログがファイルの中に行毎に混ざって存在しているとする。

"channels  ,0(0),1(3),2(?),3(-),4(R),5(-)"
"answers   ,ss,0,sd,2,us,1"
"handles    ,-10         ,10         ,102    "

ログを読むための書式文字列は下記のようになった。読んだ後の値は構造体や配列に入れる。

int ret = sscanf(buff, "%10s ,%10d ,%10d ,%10d"
                     ,enc.name,&enc.left,&enc.right,&enc.time);
char ch[6];
int ret = sscanf(buff, "%s ,0(%c),1(%c),2(%c),3(%c),4(%c),5(%c),"
              , enc.name, &ch[0], &ch[1], &ch[2], &ch[3], &ch[4], &ch[5]);
       int res[3];
       char name[32];

       int ret = sscanf(buff, "%s ,2d,%d,3d,%d,us,%d"
              , name,&res[0],&res[1],&res[2]);

vsscanfを使うには可変長引数の使い方についての知識が必要で、それを使うにはstdarg.hをインクルード下上で、下記のようなラップ関数を用意する必要がある。そのラップ関数に書式文字列と、入れ物のアドレスを渡して使う。

//書式を指定してレコードを読みこむ
void input_vsscanf(char *s, char *format, ...){
	va_list argv;

	va_start(argv, format);
	vsscanf(s, format, argv);
	va_end(argv);

	return 0;
}

//この関数からvsscanfのラップ関数を呼び出す
void logReader(char *str){
	if (NULL != strstr(str, "answers")){
		input_vsscanf(str, "%s ,2d,%d,3d,%d,us,%d"
			, name
			, &result_log.res_ss, &result_log.res_sd, &result_log.res_us);
	}
}

やっているうちにvsscanfの強みには気がついたものの、引数については相変わらず一個ずつ指定しないといけないので、いい使い方例を書くまでにはいたりませんでした。今のところ用途は無いのです が、書式文字列を変えることによって、読み込みたい値が書いてある場所を任意に指定するとか。たとえば、同じ情報が書かれたログだけれど書式がちょっとず つ違っていて、そういうときに場合分けして読みたいとか。そういう用途なのかと思っていました。

追記
vsscanf以外のvprintfや他の関数についてみていたら、どうやらデバッグ用関数を作るときに有用なことがわかりました。たとえば読み込んだ文字列から値を取り出すと同時に、ファイルに書き出すとかUDPでどこかへ転送するとか。そういうデバッグ用関数を作るときに使えそうです。

今日のところはここまで。

2016年10月 9日 (日)

C言語 可変長引数の使いかた

いままで注目していなかったが、vsscanfのついでに使えるようになっておこうと色々試しました。下記にメモを記します。


最初にva_startを呼び、どこからが可変長になっているかを知らせ、va_list型の変数に受け取らせる。上の例で行くと、関数argtameの引数はint nと...の可変長引数。

va_start(argv,n)


ということは、引数nの次から可変長引数ということを処理系に教えている。あとはva_argを呼び出すことで順番に受け取った引数が出てくる。最後にva_endを呼んで終了。

とりあえず動きを見たいので、for文を使わずに一個ずつargvが取り出される様子を見ることにした。


#include 

#include

void argtame(int n, ...){ va_list argv; va_start(argv, n); int a; a = va_arg(argv, int); printf("%d\n", a); a = va_arg(argv, int); printf("%d\n", a); va_end(argv); } void main(void){ argtame(2, 3, 4); }

もとのva_argを壊したくない場合は、一旦va_copyでコピーを作ってからやればよいとのこと。

va_copyの使い方の例(別HP)

2016年10月 2日 (日)

C言語で文字列を読むときのノウハウ

文字列を読ませる作業は結構基本的なことですが、どうやればいいか入門書にはあまり載っていませんでした。
そこで見よう見まねでscanfを使って、うまくいかずにプログラムを暴走させていました。
今思えばそこが簡単にクリアできていれば、もっと上達していたのかなと思います。。。

scanfをいきなり使うよりも、コマンドラインならgetsとかで受けて。ファイルならfgetsで一行単位でバッファにまず受けてから、そのバッファに対してsscanfというのが使えます。

sscanfというのは使い方を知っているとなかなか便利で、正規表現を知らなくても簡単に使えます。以下に例を示します。

#include 

void main(void){ FILE *fp; if (NULL == (fp = fopen("tame.txt", "r"))){ printf("fileopen error\n"); getchar(); exit(1); } char buff[256]; while (NULL != (fgets(buff, sizeof(buff), fp))){ printf("%s", buff); int x, y; int ret; ret = sscanf(buff, "%d,%d",&x,&y); printf("sscanf : %d\n",ret); if (ret == EOF){ printf("EOF returned\n"); getchar(); exit(1); } printf("x:%d , y:%d\n", x, y); } }

ファイルを開いてから、whileループの中で一行ずつbuffに読み込む。sscanfの書式を使ってbuffから2つの数値x,yを取り出す。sscanfの戻り値は、書式に沿って取り出せた値の個数となる。書式にうまく当てはまらないものがあると、書式で指定した値の個数よりも小さくなる。または0になる場合がある。エラーが出るとEOFを返すのだが、これは実際に試してみたら、書式文字列で"%d"を指定しているのに、その位置に文字列しかなかった場合など、型が全く違う場合に起こった。


具体的にログファイルを対象に読み込みを試してみた。書式文字列を色々作った。たとえばこんな文字列があったとする。

"20160613142310549,handle     ,0         ,0         ,25005     "

こんな書き方をすれば読み込めることがわかった。

int ret = sscanf(buff, "%[^,],%10s ,%10d ,%10d ,%10d"
                     ,enc.timestamp,enc.name,&enc.left,&enc.right,&enc.time);

ちなみに、CSVで文字列を読ませるときは、カンマが読んだ文字列の中に含まれないように書式文字列に工夫が必要。下記のようにすれば、1カラム目で文字列、2,3カラム目は数値を読ませられる。[^,]でカンマを読まないようにできる。さらに、[^0-9]とすれば、数字を読まなくできるし、[^a-z]なら小文字のアルファベットを読まなくできる。

ret = sscanf(buff, "%[^,],%d,%d",name,&x,&y);


補足:文字列の長さが決まっているなら、文字数の最大長を指定することもできる。

scanf("%5s",str);

これで5byteまでと指定したことになる。


補足:カラムの中に空白文字が含まれる場合。たとえば文字列の前に空白文字があれば、%sの前にスペースを入れて指定すれば、空白文字を無視した部分を文字列として読み込む。

scanf(" %s,",str);

この調子で行けば長いCSVを読むときも結構使えるのかと思い、書式の繰り返し指定ができるのかどうか探してみた。カラムが何十個も続いていたら一個ずつ指定するのが面倒だからである。

書式文字列の仕様については色々HPが出てくる。しかし具体的な使用例が載っていないので、別途探したのだが、日本語のページは中々出てこず。

printfの書式について(別HP)

stackoverflowなど、英語ページで具体的な例が色々出てきた。

書式文字列で繰り返しを指定(別HP) scanfを繰り返して使う(別HP)



※今回のCSVを読ます用途には使えないのだが、ちょっと面白い書式文字列を見かけた。
printf("%0*d\n",50,0);
とすると、0が50個並んで表示される。

結局、カラムが数十個、数百個になると、sscanfを使って一個ずつ指定するのは面倒だ。デリミタの位置を最初に検索してそれにそって文字列を読ませるなどのやり方を選択するのがよさそう。


※脱線するがpythonだったらこうする。

>>> print ('{:4} '*5).format(3,3,43,5333,6)

※Perlにもprintfがあり、よく似ているので途中間違って試してしてしまった。


sscanfを使って色々な文字列の読み込みを行いました。書式は常に固定ですが、文字数を指定して何文字目から何のデータ、その次の何文字が何のデータと細かく指定する必要が無く、atoiやstrtolなどの変換を行わずに書式指定だけで変換もやってくれるので、非常に有用です。

« 2016年5月 | トップページ | 2016年11月 »