前回作った抽出部分のサイズを取得する処理を、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$
まあ、当然かもしれませんが、勝てました。
これからいろいろ処理を作りこんでいくので、だんだん重たくなって、本物に追いつかないようになると思いますが。
つづく


コメント