自作のtail(その8)

前回作った抽出部分のサイズを取得する処理を、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$

まあ、当然かもしれませんが、勝てました。
これからいろいろ処理を作りこんでいくので、だんだん重たくなって、本物に追いつかないようになると思いますが。

つづく

コメント

タイトルとURLをコピーしました