前回作った抽出部分のサイズを取得する処理を、get_tailsizeという関数名で関数化しました。
1 #include <stdio.h> 2 #include <sys/types.h> //stat 3 #include <sys/stat.h> //stat 4 #include <unistd.h> //stat 5 #include <getopt.h> //getopt 6 #include <stdlib.h> //atoi 7 #include <fcntl.h> //open 8 9 int get_tailsize(int fd,int filesize,int req_lines) 10 { 11 char buf[10],*bufp; 12 int bsize=sizeof(buf); 13 int i,size,bno,this_size,lf_num=0; 14 15 for(bno=0; bno <= filesize/bsize; bno++){ 16 size = bsize*(bno+1); 17 size = filesize < size ? filesize : size; 18 lseek(fd, -size, SEEK_END); 19 20 size = size % bsize; 21 size = size > 0 ? size : bsize; 22 bufp = buf; 23 while(1){ 24 this_size = read(fd, bufp, size - (bufp-buf)); 25 if(this_size == 0)break; 26 bufp += this_size; 27 } 28 29 for(i = 0; i < size; i++){ 30 if(buf[size-i-1] == 0x0a){ 31 if(!((bno == 0) && (i==0))){ 32 lf_num++; 33 } 34 if(lf_num == req_lines){ 35 return bsize*bno+i; 36 } 37 } 38 } 39 } 40 return filesize; 41 } 42 43 int main(int argc, char* argv[]) 44 { 45 int fd; 46 char *fname; 47 struct stat fs; 48 49 char wbuf[100]; //for output 50 int lf_num=0; 51 int req_lines=10; 52 int lf_index; 53 int i,len,size,bno; 54 55 fname = argv[1]; 56 req_lines = atoi(argv[2]); 57 58 stat(fname, &fs); 59 60 fd=open(fname, O_RDONLY); 61 62 size = get_tailsize(fd, fs.st_size, req_lines); 63 64 lseek(fd, -size, SEEK_END); 65 len = read(fd, wbuf, size); 66 67 for(i = 0; i < len; i++){ 68 putchar((int)wbuf[i]); 69 } 70 close(fd); 71 72 return 0; 73 }
次に、64~69行目を関数化して、ファイルを少しずつバッファへ読み込んで、値を標準出力する処理を作ります。
9 int get_tailsize(int fd,int filesize,int req_lines) 10 { ~変更なし~ 41 } 42 43 void out_taillines(int fd,int size) 44 { 45 char buf[10],*bufp; 46 int bsize=sizeof(buf); 47 int read_size,this_size; 48 49 lseek(fd, -size, SEEK_END); 50 51 while(1){ 52 bufp = buf; 53 read_size = bsize <= size ? bsize : size % bsize; 54 55 this_size = read(fd, buf, read_size); 56 size -= this_size; 57 while(this_size--) 58 putchar((int)*bufp++); 59 60 if(size <= 0) 61 break; 62 } 63 } 64 65 int main(int argc, char* argv[]) 66 { 67 int fd; 68 char *fname; 69 struct stat fs; 70 71 int req_lines=10; 72 int size; 73 74 fname = argv[1]; 75 req_lines = atoi(argv[2]); 76 77 stat(fname, &fs); 78 79 fd=open(fname, O_RDONLY); 80 81 size = get_tailsize(fd, fs.st_size, req_lines); 82 83 out_taillines(fd, size); 84 85 close(fd); 86 87 return 0; 88 }
これで、処理の中核はできたつもり。
確認してみます。
takk@deb9:~/tmp$ cat a.txt 1 2 3 4 5 6 7 8 9 10 11 12 takk@deb9:~/tmp$ gcc t.c takk@deb9:~/tmp$ ./a.out a.txt 3 10 11 12 takk@deb9:~/tmp$
約420Mの大きなファイルで確認します。
takk@deb9:~/tmp$ seq 50000000 > b.txt takk@deb9:~/tmp$ ls -lh b.txt -rw-r--r-- 1 takk takk 419M 6月 10 15:30 b.txt takk@deb9:~/tmp$
ファイルを全部読み込んで、末尾だけ表示するのは、おそらくsedで末尾行を抽出する処理イメージに近いと思うので、sedの処理時間と比べてみます。
sedでは、
takk@deb9:~/tmp$ time sed -ne '/50000000/p' b.txt 50000000 real 0m3.838s user 0m2.424s sys 0m0.052s takk@deb9:~/tmp$
やはりファイルが大きいと時間がかかります。
さて、腕試し。自作tailは、どのぐらいの処理時間になるか。
takk@deb9:~/tmp$ time ./a.out b.txt 1 50000000 real 0m0.001s user 0m0.000s sys 0m0.000s takk@deb9:~/tmp$
早い!
さすがバッファ10Byte。
今の自作tailだったら、本物に勝てるかもしれません。
ということで、本物tailで確認。
takk@deb9:~/tmp$ time tail -1 b.txt 50000000 real 0m0.004s user 0m0.000s sys 0m0.000s takk@deb9:~/tmp$
まあ、当然かもしれませんが、勝てました。
これからいろいろ処理を作りこんでいくので、だんだん重たくなって、本物に追いつかないようになると思いますが。
つづく
コメント