自作のtail(その2)

自作tail続きです。

前回は、ファイル全体を読み込んで、末尾10行を表示するプログラムを作りました。
末尾部分の抽出だけでなく、中間部分や、行指定して抽出する機能も入れていく予定です。まあ、何より、tailの肝である、ファイルを後方から読み込み始める機能が一番に必要ですが。

さて今回は、そのファイル後方から読み込み始めるためのfseekについて、簡単に動きを確認していこうと思います。

このようなプログラムで実験します。

takk@deb9:~/tmp$ cat -n t.c
     1	#include <stdio.h>
     2	
     3	int main()
     4	{
     5		int c;
     6	
     7		FILE *fp;
     8	
     9		fp=fopen("a.txt","r");
    10		fseek(fp,-5,SEEK_END);
    11		while((c=fgetc(fp)) != -1){
    12			putchar(c);
    13		}
    14		fclose(fp);
    15	
    16		return 0;
    17	}
takk@deb9:~/tmp$ 

10行目でfseekを使っていますが、第3引数にSEEK_ENDが指定されています。これでファイル末尾を指定します。第2引数は、第3引数で指定した位置からのオフセットを指定するので、-5を指定すると、ファイルの末尾から数えて5 Byte前を指定したことになります。
要するに、このプログラムは、ファイル末尾5 Byteを表示するプログラムです。

実行してみます。

takk@deb9:~/tmp$ seq 20 >a.txt
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
9
20
takk@deb9:~/tmp$ 

2行だけ表示されましたが、改行がそれぞれ1Byte入るので、3文字+2改行で5Byteが表示されたということになります。

次に、オフセットにファイルサイズよりも大きな数字を指定してみます。
-1000を指定し、合わせてperrorでエラーメッセージも表示してみます。

takk@deb9:~/tmp$ cat -n t.c
     1	#include <stdio.h>
     2	
     3	int main()
     4	{
     5		int c;
     6	
     7		FILE *fp;
     8	
     9		fp=fopen("a.txt","r");
    10		if(fseek(fp,-1000,SEEK_END) != 0){
    11			perror("ERROR");
    12		}
    13		while((c=fgetc(fp)) != -1){
    14			putchar(c);
    15		}
    16		fclose(fp);
    17	
    18		return 0;
    19	}
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
ERROR: Invalid argument
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
takk@deb9:~/tmp$ 

ファイル全体が表示されました。fseekで末尾から-1000を指定したので、fseekの結果でファイル読み出し位置が先頭に移動したように見えるかもしれません。
しかしエラーが返っていることから分かるように、fseekは失敗しています。つまりファイル読み出し位置は初期値であるファイル先頭のまま移動していなかった、ということになります。

確認のためfseekを2回使ってみます。1回目は成功させ、2回目で失敗させています。

takk@deb9:~/tmp$ cat -n t.c
     1	#include <stdio.h>
     2	
     3	int main()
     4	{
     5		int c;
     6	
     7		FILE *fp;
     8	
     9		fp=fopen("a.txt","r");
    10		if(fseek(fp,-5,SEEK_END) != 0){
    11			perror("ERROR");
    12		}
    13		if(fseek(fp,-1000,SEEK_CUR) != 0){
    14			perror("ERROR");
    15		}
    16		while((c=fgetc(fp)) != -1){
    17			putchar(c);
    18		}
    19		fclose(fp);
    20	
    21		return 0;
    22	}
takk@deb9:~/tmp$ vi t.c
takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
ERROR2: Invalid argument
9
20
takk@deb9:~/tmp$ 

1回目のfseekで、ファイル読み出し位置が、末尾から5 Byte前に変更され、2回目のfseekではエラーとなり、読み出し位置は変わってないので、同じく末尾5Byteが表示されました。

次はftellで、現在のファイル読み出し位置を確認しつつ、fseekを使っていきます。

takk@deb9:~/tmp$ cat -n t.c
     1	#include <stdio.h>
     2	
     3	int main()
     4	{
     5		int c;
     6	
     7		FILE *fp;
     8	
     9		fp=fopen("a.txt","r");
    10		printf("ftell=%u\n",ftell(fp));
    11		if(fseek(fp,0,SEEK_END) != 0){
    12			perror("ERROR1");
    13		}
    14		printf("ftell=%u\n",ftell(fp));
    15		if(fseek(fp,-5,SEEK_END) != 0){
    16			perror("ERROR1");
    17		}
    18		printf("ftell=%u\n",ftell(fp));
    19		if(fseek(fp,-1000,SEEK_CUR) != 0){
    20			perror("ERROR2");
    21		}
    22		printf("ftell=%u\n",ftell(fp));
    23		while((c=fgetc(fp)) != -1){
    24			putchar(c);
    25		}
    26		fclose(fp);
    27	
    28		return 0;
    29	}
takk@deb9:~/tmp$ 

fopenしてすぐに、ftellすると、読み出し位置は先頭である0が表示されます。

takk@deb9:~/tmp$ gcc t.c
takk@deb9:~/tmp$ ./a.out
ftell=0
ftell=51
ftell=46
ERROR2: Invalid argument
ftell=46
9
20
takk@deb9:~/tmp$ 

fseekをSEEK_ENDをオフセット0で実行すると、ファイル末尾である51という数字が表示されました。
次にfseekをSEEK_ENDをオフセット-5で実行しているので、51から5を引いた数、46と表示されました。

当然ですが、ファイル末尾の読み込み位置は、ファイルサイズと同じ数字が得られます。

takk@deb9:~/tmp$ wc -c a.txt
51 a.txt
takk@deb9:~/tmp$ 

続く

Leave a Reply

Your email address will not be published. Required fields are marked *

CAPTCHA