システム・プログラム 電子・情報工学系 新城 靖 <yas@is.tsukuba.ac.jp>
このページは、次の URL にあります。
http://www.coins.tsukuba.ac.jp/~yas/classes/syspro-2003/2003-04-21
あるいは、次のページから手繰っていくこともできます。
http://www.coins.tsukuba.ac.jp/~yas/
http://www.is.tsukuba.ac.jp/~yas/index-j.html
A 1 B 2 C 3 ... Z 26モールス符合。
ポケットベル・コード。
コンピュータでよく使われる文字集合と文字の符号化の方法
数の値と数を画面に文字として表示するために使う符合が違うことに注意する。 数の0と文字の0は違う。
8ビット符号。 20(16進)から7F(16進)までは、ASCII と同じ。 西ヨーロッパでよく使われている。
参考:
http://www.sandia.gov/sci_compute/iso_symbol.html
漢字1文字を、7ビットと7ビットの組でで表す。それぞれ 21〜7E に入るよ うになっている。16進で2121から747Eまでの範囲にある。ただし、半分は空い ている。
参考:
http://www.hlla.is.tsukuba.ac.jp/~yas/classes/ipe/nitiniti2-enshu-1996/1996-11-18/kanji-code.html
int x ; x = 'A' ; /* (1) */ x = 0x41 ; /* (2) */漢字を「''」で囲んだ時に何が起るかは不明である。 コンパイラはエラーを出さない。
int x ; x = 'A' ; /* (1) */ x = "A" ; /* (2) */
char の配列として表現される。連続番地に置かれ、0終端されている。ダブ ルクォート「""」で囲むと、名前がない配列が作られ、その先頭番地が入る。
漢字を「""」で囲んだ時には、EUC だと比較的そのまま通る。 JIS だと、「"」や「\」と重なるバイトを持つ漢字が現れた時に問題が生じる。
C言語で "" で括った文字列を指定すると、名前がない charの配列が作られ、 その先頭番地が返される。配列の内容は、指定された文字と、最後に文字列の 終端を意味する 0 が付く。表面的に見える文字の数より 1 バイト多いことに 注意する。
---------------------------------------------------------------------- 1: /* 2: string-array.c -- 文字列と配列 3: ~yas/syspro/cc/string-array.c 4: Start: 2002/04/21 18:24:34 5: */ 6: 7: char h1[] = { 'h','e','l','l','o',0 }; 8: 9: main() 10: { 11: int i ; 12: printf("%s\n",h1 ); 13: printf("0x%x, 0x%x\n",h1,"hello" ); 14: for( i=0 ; i<5 ; i++ ) 15: { 16: printf("[%c]", h1[i] ); 17: } 18: printf("\n"); 19: for( i=0 ; i<5 ; i++ ) 20: { 21: printf("[%c]", "hello"[i] ); 22: } 23: printf("\n"); 24: } ----------------------------------------------------------------------
配列なので、"hello"[i] のようなこともできる。
printf で %s と指定すると、その番地から 0 の直前までの内容を 画面に出力する。
実行結果。
---------------------------------------------------------------------- % cp ~yas/syspro/cc/string-array.c .% make string-array
cc string-array.c -o string-array % ./string-array
hello 0x80495b8, 0x804858c [h][e][l][l][o] [h][e][l][l][o] %
----------------------------------------------------------------------
#include <string.h> 文字列の長さを調べる(終端の 0 は、長さに含まれない) size_t strlen(const char *s); 文字列のコピー char *strcpy(char *dest, const char *src); char *strncpy(char *dest, const char *src, size_t n); char *strdup(const char *s); 文字列の連結 char *strcat(char *dest, const char *src); char *strncat(char *dest, const char *src, size_t n); 文字列の比較 int strcmp(const char *s1, const char *s2); int strncmp(const char *s1, const char *s2, size_t n); int strcasecmp(const char *s1, const char *s2); int strncasecmp(const char *s1, const char *s2, size_t n);str"n"cpy() は、n バイトだけコピーする。n より短かったら、残りの部分に 0 を埋める。str"n"cat() も同様である。あまりよいインタフェースではない。
str"case"cmp では、大文字(upper case)と小文字(lower case)の区別をしな い。
OpenBSD, FreeBSD, SunOS 5.8 には、より優れた API がある。
size_t strlcat(char *dst, char *src, size_t dstsize); size_t strlcpy(char *dst, char *src, size_t dstsize);strlcat() は、strncat() と似ているが、最後に 0 を埋めることはない。
その他
文字列中の文字や文字列の検索 char *strchr(const char *s, int c); char *strrchr(const char *s, int c); char *strstr(const char *haystack, const char *needle); char *index(constchar*"s, int c); char *rindex(const char *s, int c); 文字列中のトークンへの分解。 char *strtok(char *s, const char *delim); その他 int strcoll(const char *s1, const char *s2); size_t strcspn(const char *s, const char *reject); char *strfry(char *string); char *strpbrk(const char *s, const char *accept); char *strsep(char **stringp, const char *delim); size_t strspn(const char *s, const char *accept); size_t strxfrm(char *dest, const char *src, size_t n);strcpy(), strcat() の代りに次の関数も便利である。
#include <stdio.h> int sprintf(char *str, const char *, ...); int snprintf(char *str, size_t size, const char *,...); #include <stdag.h> int vsprintf(char *str, const char *, va_list ap); int vsnprintfvsnprintf(char *str, size_t size, const char *, va_list ap);
---------------------------------------------------------------------- 1: /* 2: string-cpycat.c -- 文字列のコピーと結合(悪い例) 3: ~yas/syspro/cc/string-cpycat.c 4: Start: 2002/04/21 23:02:39 5: */ 6: 7: #include <stdio.h> /* stderr */ 8: #include <stdlib.h> /* malloc() */ 9: #include <string.h> /* strcpy(), strcat(), strlen() */ 10: 11: main() 12: { 13: char buf[100]; 14: strcpy(buf,"hello"); 15: strcat(buf,",world"); 16: strcat(buf,"\n"); 17: if( strlen(buf) >= 99 ) /* too late */ 18: { 19: fprintf(stderr,"buffer overflow\n"); 20: exit( 1 ); 21: } 22: printf("%s",buf ); 23: free( buf ); 24: } ----------------------------------------------------------------------
注意:この例は、悪い例なので真似をしてはいけない。
strcpy() で初期化して、strcat() で後ろに付け加えていく。
100 バイトのバッファでは、buf[0] からbuf[98]まで文字のデータが入れられる。 最後のbuf[99] には、文字ではなく0 を入れる。buf[100] は使ってはいけない。 (次の別のデータが入っている。)
strlen(buf) が 100 を越えた時には、使ってはいけない場所を使ってしまった ことを意味する。これを、「バッファ・オーバーフロー」という。 そもそも事後にチェックするのは、本当は遅い。そもそも、バッファ・オーバー フローが起きないように気を付けながらプログラムを書くべきである。
---------------------------------------------------------------------- 1: /* 2: string-snprintf.c -- snprintf を使った文字列のコピーと結合 3: ~yas/syspro/cc/string-snprintf.c 4: Start: 2002/04/21 23:02:39 5: */ 6: 7: #include <stdio.h> /* stderr, snprintf() */ 8: #include <stdlib.h> /* malloc() */ 9: 10: main() 11: { 12: char buf[100]; 13: if( snprintf(buf,100,"hello%s\n",",world") >=sizeof(buf) ) 14: { 15: fprintf(stderr,"buffer overflow\n"); 16: exit( 1 ); 17: } 18: printf("%s",buf ); 19: } 20: ----------------------------------------------------------------------snprintf() は、第1引数に、バッファの番地、第2引数にバッファの長さ(最 後の0を含む)をとる。第3引数以降は、printf() と同じである。snprintf() では、けっしてバッファ・オーバーフローは起きない。起きそうになると、負 の数を返す。(成功すると、バイト数(最後の0は含まない)を返す。)
snprintf() は、%s 以外に %d や %c も使える。
注意:Linux (Glibc を使っている)では、snprintf() の仕様が変更された。 古い仕様(glibc 2.0.6以前)では、snprinf() は、エラーが起きると -1 を返 す。新しい仕様(glibc 2.1以降)では、snprintf() は、必要なバイト数、すな わち、バッファが無限大であるときに書き込まれる文字列の長さ(最後の0を除 く)を返す。これは、strlen() が返すものと同じである。従って、新しい snprintf() では、結果とバッファの大きさを比較して正確に書き込まれたか を検査する。(snprintf() は、決してバッファ・オーバーフローを起こすこと はないが、バッファが足りない時には意図していない結果が保存されているこ とになる。)
---------------------------------------------------------------------- 1: 2: /* 3: file-copy.c -- ファイルをコピーする簡単なプログラム 4: ~yas/syspro/file/file-copy.c 6: Start: 1995/03/04 16:40:24 7: */ 8: 9: #include <stdio.h> /* stderr */ 10: #include <fcntl.h> /* open(2) */ 11: 12: extern void file_copy( char *from_name, char *to_name ); 13: 14: main( int argc, char *argv[] ) 15: { 16: if( argc != 3 ) 17: { 18: fprintf( stderr,"Usage: %s from to\n", argv[0] ); 19: exit( 1 ); 20: } 21: file_copy( argv[1],argv[2] ); 22: } 23: 24: #define BUFFERSIZE 1024 25: 26: void file_copy( char *from_name, char *to_name ) 27: { 28: int from_fd,to_fd ; 29: char buffer[BUFFERSIZE] ; 30: int rcount ; 31: int wcount ; 32: 33: from_fd = open( from_name,O_RDONLY ); 34: if( from_fd == -1 ) 35: { 36: perror( from_name ); 37: exit( 1 ); 38: } 39: to_fd = open( to_name,O_WRONLY|O_CREAT|O_TRUNC,0666 ); 40: if( to_fd == -1 ) 41: { 42: perror( to_name ); 43: exit( 1 ); 44: } 45: while( (rcount=read(from_fd,buffer,BUFFERSIZE)) > 0 ) 46: { 47: if( (wcount=write(to_fd,buffer,rcount))!= rcount ) 48: { 49: perror( to_name ); 50: exit( 1 ); 51: } 52: } 53: close( from_fd ); 54: close( to_fd ); 55: } ----------------------------------------------------------------------
プログラムに引数を渡すことができる。argv[0] には、プログラムの名前を示 す文字列が入っている。自分のプログラムの名前で動作を変える時にだけ参照 する。argv[1] 以降が本当の意味での引数である。argc には、argv[0]を含め た引数の数が入っている。
open()は、ファイルを開くシステム・コールである。O_RDONLYとは、 読み込み専用でファイルを開くことを意味している。open() システム・コー ルは、結果としてファイル記述子を返す。ファイル記述子は、負でない小さな 整数(だいたい0〜1024の範囲、最大はシステムによって違う)である。 ファイル記述子は、read()システム・コールやwrite()システム・コールで実 際にファイルを読み書きする時に使われる。
エラーが起きた時には、open() システム・コールは、-1 を返す。この時、エ ラーの番号が errno という変数に格納される。perror() は、errno変数を解 析して、より詳しいエラー・メッセージを表示する関数である。perror() の 引数は、エラー・メッセージとともに表示される文字列である。
ここでは、出力用のファイルを開いている。O_WRONLYは、書き込み専用でファ イルを開くことを意味している。O_CREATは、ファイルが存在しなければ作る ように指示するものである。ファイルが存在する場合、上書きされる。 O_TRUNCは、ファイルが存在している時には、その大きさを0にすることを指 示するものである。0666 (C言語で0から始まる数は、8進数)は、ファイルを 作る時のモードである。この数値に従って、ファイルのモード(ls -l で rwxrwxrwx と表示される部分)が決定される。作成されるファイルのモードは、 ここで指定されたモードから現在の umask が落とされた(引かれた)値とな る。
read() システム・コールは、第1引数で指定されたファイル記述子のファイ ルを読み込み、それを第2引数の番地へ保存する。読み込むバイト数は、第3 引数で与えられる。結果として読み込んだバイト数を返す。通常は、BUFFERSIZE が返される。read() システム・コールの結果、ファイル上の読み書きする位 置が、実際に読み込んだバイト数だけずれる。ファイルの末尾近くや、ファイ ルが端末の時、BUFFERSIZE 以下の値が返される。ファイルの末尾に行き着くと 0 が返される。エラーが起きると、-1 が返される。
write() システム・コールは、第1引数で指定されたファイル記述子のファイ ルへデータを書き込む。書き込まれるデータは、第2引数で与えられた番地か ら、第3引数で与えらた大きさである。write() システム・コールは、結果と して書き込んだバイト数を返す。ファイル上の読み書きする位置が、実際に読 み込んだバイト数だけずれる。通常は、BUFFERSIZE が返される。空き容量不足 などで書き込みが失敗した時には、-1 を返す。エラーが起きると、-1 が返さ れる。
close() は、ファイルを閉じるシステム・コールである。このシステム・コールを実行してしまうと、そのファイル記述子は無効になる。すなわち、read() や write() を行うと、エラーになる。
---------------------------------------------------------------------- % mkdir file% cd file
% cp ~yas/syspro/file/file-copy.c .
% make file-copy
cc file-copy.c -o file-copy % ls
file-copy file-copy.c % echo "This is file1" > file1
% cat file1
This is file1 % ./file-copy file1 file2
% ls
file-copy file-copy.c file1 file2 % ls -l
総ブロック数 30 -rwxr-xr-x 1 yas lab 12616 4月 22日 17時19分 file-copy -rw-r--r-- 1 yas lab 1096 4月 22日 17時19分 file-copy.c -rw-r--r-- 1 yas lab 14 4月 22日 17時20分 file1 -rw-r--r-- 1 yas lab 14 4月 22日 17時20分 file2 % diff file1 file2
% cmp file1 file2
% cat file2
This is file1 %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: /* 2: fd-print.c -- ファイル記述子を表示するプログラム 3: ~yas/syspro/file/fd-print.c 5: Start: 1997/04/21 18:23:13 6: */ 7: 8: #include <fcntl.h> 9: 10: void main( int argc, char *argv[] ) 11: { 12: int i ; 13: int fd ; 14: for( i=1 ; i<argc ; i++ ) 15: { 16: fd = open( argv[i],O_RDONLY ); 17: printf("%s: %d\n",argv[i],fd ); 18: } 19: } ----------------------------------------------------------------------
---------------------------------------------------------------------- % ./fd-print fd-print.cfd-print.c: 3 % ./fd-print fd-print.c /etc/passwd fd-print.c
fd-print.c: 3 /etc/passwd: 4 fd-print.c: 5 %
----------------------------------------------------------------------
プログラムが実行される時、open() システム・コールを使わなくても、次の 3つのファイル記述子は使える状態になっている。
stdio-thru.cは、標準入力で指定されたファイルの内容を標準出力で指定され たファイルへコピーするプログラムである。
---------------------------------------------------------------------- 1: 2: /* 3: stdio-thru.c -- 標準入力から標準出力へのコピー 4: ~yas/syspro/file/stdio-thru.c 6: Start: 1995/03/04 16:40:24 7: */ 8: 9: #include <stdio.h> 10: 11: extern void stdio_thru(void); 12: 13: main( int argc, char *argv[] ) 14: { 15: if( argc != 1 ) 16: { 17: fprintf( stderr,"Usage: %s\n", argv[0] ); 18: exit( 1 ); 19: } 20: stdio_thru(); 21: } 22: 23: #define BUFFERSIZE 1024 24: 25: void stdio_thru() 26: { 27: char buffer[BUFFERSIZE] ; 28: int rcount ; 29: int wcount ; 30: 31: while( (rcount=read(0,buffer,BUFFERSIZE)) > 0 ) 32: { 33: if( (wcount=write(1,buffer,rcount))!= rcount ) 34: { 35: perror("stdout"); 36: exit( 1 ); 37: } 38: } 39: close( 0 ); 40: close( 1 ); 41: } ----------------------------------------------------------------------このプログラムは、引数をとらない。"<" や ">" は、シェルにより解釈され、 このプログラムには渡されない。
file_copy() とは異なり、stdio_thru() では、ファイルを開く操作(open()) を行うことなく、入出力(read(),write)を行っている。このような事が可能 な理由は、UNIXでは、ファイル記述子 0, 1, 2 は、シェルにより既に開 かれているからである。
ファイル記述子 0 は、なにもしないと端末のキーボードに割り当てられてい る。ファイル記述子 1, 2 は、端末の画面に割り当てられている。しかし、 "<"、">"、"|"を使うと別のファイル やパイプに切替えられる。
---------------------------------------------------------------------- % ./stdio-thruljd
ljd sk
sk ^D % ls bbb
bbb がアクセスできません: そのようなファイルまたはディレクトリはありません % ./stdio-thru < stdio-thru.c > bbb
% ls -l stdio-thru.c bbb
-rw-r--r-- 1 yas lab 690 4月 22日 17時37分 bbb -rw-r--r-- 1 yas lab 690 4月 22日 17時35分 stdio-thru.c %
----------------------------------------------------------------------
---------------------------------------------------------------------- 1: /* 2: filter-char.c -- 文字単位の簡単なフィルタ 3: ~yas/syspro/file/filter-char.c 5: Start: 1997/04/21 18:23:13 6: */ 7: 8: #include <stdio.h> /* stdin, stdout, fopen(), fclose(), getc(), */ 9: #include <ctype.h> /* toupper() */ 10: 11: extern void filter_char_upper( FILE *in, FILE *out ); 12: 13: main( int argc, char *argv[] ) 14: { 15: int i ; 16: FILE *fp ; 17: if( argc == 1 ) 18: filter_char_upper( stdin, stdout ); 19: else 20: { 21: for( i=1 ; i< argc ; i++ ) 22: { 23: if( (fp = fopen( argv[i],"r" )) == NULL ) 24: { 25: perror( argv[i] ); 26: exit( -1 ); 27: } 28: filter_char_upper( fp, stdout ); 29: fclose( fp ); 30: } 31: } 32: } 33: 34: void filter_char_upper( FILE *in, FILE *out ) 35: { 36: int c ; /* int型。char は、不可 */ 37: while( (c=getc(in)) != EOF ) 38: { 39: putc( toupper(c),out ); 40: } 41: } ----------------------------------------------------------------------
実行例
---------------------------------------------------------------------- % ./filter-charabc
ABC asdfjkl;
ASDFJKL; ^D %
----------------------------------------------------------------------
fopen()は、ファイルを開くライブラリ関数である。"r" は、 読み込み専用で ファイルを開くことを意味している。fopen()は、結果として FILE 構造体へ のポインタを返す。これは、次のようなライブラリ関数で使われる。
fgetc() fputs() getc() getw() fgets() fread() ungetc() fprintf() fscanf() fputc() fwrite() putc() putc() fflush() fseek() ftell() frewind() fclose() fdopen() freopen() ferrror() feof() clearerr() fileno()
このような、FILE * を使うライブラリ関数群は、次のような名前で呼ば れる。
putc() は、第1引数で指定された文字を第2引数で与えられたストリームへ 出力する。
fclose() は、ファイルを閉じるライブラリ関数である。
Java 風に書くとこうなる。
class FILE { int fgetc(); int fputs(String str); size_t fread(byte buf, size_t size, size_t nmemb); ... } FILE stdin ; FILE stdout ; int x = stdin.fgetc(); stdout.fputs("hello");引数の FILE *stream が、オブジェクトとなり、普通の引数からは消える。呼 出しの時には、オブジェクトを指定する。
---------------------------------------------------------------------- 1: /* 2: filestruct-print.c -- _filestruct[] の内容を表示する 3: ~yas/syspro/file/filestruct-print.c 4: Start: 1997/05/05 18:31:55 5: */ 6: 7: #include <stdio.h> 8: 9: #if 0 10: 11: struct _IO_FILE { 12: int _flags; /* High-order word is _IO_MAGIC; rest is flags. */ 13: #define _IO_file_flags _flags 14: 15: /* The following pointers correspond to the C++ streambuf protocol. */ 16: /* Note: Tk uses the _IO_read_ptr and _IO_read_end fields directly. */ 17: char* _IO_read_ptr; /* Current read pointer */ 18: char* _IO_read_end; /* End of get area. */ 19: char* _IO_read_base; /* Start of putback+get area. */ 20: char* _IO_write_base; /* Start of put area. */ 21: char* _IO_write_ptr; /* Current put pointer. */ 22: char* _IO_write_end; /* End of put area. */ 23: char* _IO_buf_base; /* Start of reserve area. */ 24: char* _IO_buf_end; /* End of reserve area. */ 25: /* The following fields are used to support backing up and undo. */ 26: char *_IO_save_base; /* Pointer to start of non-current get area. */ 27: char *_IO_backup_base; /* Pointer to first valid character of backup area */ 28: char *_IO_save_end; /* Pointer to end of non-current get area. */ 29: 30: struct _IO_marker *_markers; 31: 32: struct _IO_FILE *_chain; 33: 34: int _fileno; 35: int _blksize; 36: _IO_off_t _old_offset; /* This used to be _offset but it's too small. */ 37: 38: #define __HAVE_COLUMN /* temporary */ 39: /* 1+column number of pbase(); 0 is unknown. */ 40: unsigned short _cur_column; 41: signed char _vtable_offset; 42: char _shortbuf[1]; 43: 44: /* char* _save_gptr; char* _save_egptr; */ 45: 46: _IO_lock_t *_lock; 47: }; 48: 49: typedef struct _IO_FILE _IO_FILE; 50: typedef struct _IO_FILE FILE; 51: 52: extern FILE *stdin; 53: extern FILE *stdout; 54: extern FILE *stderr; 55: 56: #endif 57: 58: extern void filestruct_print( FILE *fp ); 59: 60: main() 61: { 62: FILE *fp ; 63: 64: printf("stdin == 0x%08x (fd==%d)\n",stdin, stdin->_fileno ); 65: printf("stdout == 0x%08x (fd==%d)\n",stdout, stdout->_fileno ); 66: printf("stderr == 0x%08x (fd==%d)\n",stderr, stderr->_fileno ); 67: 68: fp = fopen("filestruct-print.c","r"); 69: printf("fp == 0x%08x (fd==%d)\n",fp, fp->_fileno ); 70: 71: printf("fopen()\n"); 72: filestruct_print( fp ); 73: fgetc( fp ); 74: printf("fgetc()\n"); 75: filestruct_print( fp ); 76: fgetc( fp ); 77: printf("fgetc()\n"); 78: filestruct_print( fp ); 79: fclose( fp ); 80: printf("fclose()\n"); 81: filestruct_print( fp ); 82: } 83: 84: 85: void filestruct_print( FILE *fp ) 86: { 87: printf("read_ptr:0x%08x, read_end:0x%08x, read_base:0x%08x\n", 88: fp->_IO_read_ptr, fp->_IO_read_end, fp->_IO_read_base ); 89: } ----------------------------------------------------------------------
fopen() した直後は、read_ptr などは初期化されていない。 最初に fgetc() を実行した時に初期化される。 fgetc() を呼び出すと、read_ptr が1つずれる。 fclose() すると、read_ptr などは 0 に初期化される。---------------------------------------------------------------------- % make filestruct-printcc filestruct-print.c -o filestruct-print % ./filestruct-print
stdin == 0x40146fc0 (fd==0) stdout == 0x40147140 (fd==1) stderr == 0x401472c0 (fd==2) fp == 0x08049958 (fd==3) fopen() read_ptr:0x00000000, read_end:0x00000000, read_base:0x00000000 getc() read_ptr:0x40019001, read_end:0x400199fb, read_base:0x40019000 getc() read_ptr:0x40019002, read_end:0x400199fb, read_base:0x40019000 fclose() read_ptr:0x00000000, read_end:0x00000000, read_base:0x00000000 %
----------------------------------------------------------------------
キーボードで打てるのは、ASCII 文字の並びである。キーボードから int を 読みたい時には、文字の並びとしてまず読み込み(fgets())、次に、 文字の並びから整数に変換する(atoi(), strtol(), sscanf())
scanf() は、人間が打つ、間違い(意図的な攻撃)を含む可能性がある場所で 使うべきではない。
OSの中には、キーボードからASCII 文字の並び('0' から'9')を打ち込むと、OSの中で整に変換する機能が あるものもある。Unix には、そのような機能はない。文字の並びではなく数 が必要ならば、ライブラリ関数で変換する。
特殊な状況では、混ぜる必要がでてくる。 その時は、次のような関数を使う。
この機能を抑止するには、stty cbreak とするシステムが多い。Irix など、 cbreak が使えないシステムもある。
stty raw でも、バッファリングがなくなるが、^C による強制終了もできなく なるので、注意する。---------------------------------------------------------------------- % cathello
hello ^D % stty cbreak;cat
hheelllloo
^C %
----------------------------------------------------------------------
---------------------------------------------------------------------- % catabc
abc ^D % stty raw; cat
aabbcc (^Dが利かない) (別の端末から ps して kill) Terminated %
----------------------------------------------------------------------
UTMP_FILE
」
または、「WTMP_FILE
」という文字列を含む行を、ファイル
/usr/include/utmp.h
から探し、結果を表示している。
一般的には、次のようになる。---------------------------------------------------------------------- % egrep '_PATH_[UW]TMP' /usr/include/paths.h#define _PATH_UTMP "/var/run/utmp" #define _PATH_WTMP "/var/log/wtmp" %
----------------------------------------------------------------------
---------------------------------------------------------------------- % egrep pattern file1 file2 ...----------------------------------------------------------------------
pattern
としては、次のような正規表現(regular
expressions)が使える。
「*」、「$」、「[]」など、シェルが展開してしまう文字をパタンとして指定 する時には、シングル・クォート「''」で括る。
egrep は、grep の拡張版である。egrep は、grep より機能が拡張され、アル ゴリズムも改良されていて速い。fgrep は、grep の簡易版で、正規表現が使 えない(速度も遅い)。
高水準入出力ライブラリ関数を持ちいて、次のようなフィルタを作りなさい。
入力: ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 出力: NOPQRSTUVWXYZABCDEFGHIJKLM nopqrstuvwxyzabcdefghijklmそれ以外のものは、そのまま出力する。この暗号法を rot13 (rotation)とい う。これは、まったく同じルーチンで暗号化と復号化ができる。 ネットワーク・ニュースなでど、ネタばらしの投稿に使われる。
fgrep は、grep, egrep などと違って単純な文字列の比較しかできない。 regcomp(3) などを使えば、正規表現の検索部分は自分で作らなくてもよい。
以下に rot-N 暗号プログラムの main() 関数の例を示す。
---------------------------------------------------------------------- 1: /* 2: rotn.c -- 文字単位の簡単なフィルタ(rotN) 3: ~yas/syspro/file/rotn.c 4: Start: 2003/04/21 13:31:46 5: */ 6: 7: #include <stdio.h> /* stdin, stdout, fopen(), fclose(), getc(), */ 8: #include <ctype.h> /* isalpha() */ 9: #include <stdlib.h> /* strtol() */ 10: 11: main( int argc, char *argv[] ) 12: { 13: int n ; 14: char *filename ; 15: if( argc != 3 ) 16: { 17: fprintf(stderr,"Usage: %% %s n file\n",argv[0]); 18: exit( 1 ); 19: } 20: n = strtol( argv[1],0,10 ); 21: filename = argv[2] ; 22: printf("n==%d, file == %s \n", n, filename ); 23: } ----------------------------------------------------------------------以下に fgrep プログラムの main() 関数の例を示す。
---------------------------------------------------------------------- 1: /* 2: myfgrep.c -- 簡単な行単位のフィルタ(fgrep) 3: ~yas/syspro/file/myfgrep.c 4: Start: 2003/04/23 14:51:27 5: */ 6: 7: #include <stdio.h> /* stdin, stdout, fopen(), fclose(), getc(), */ 8: #include <string.h> /* strstr() */ 9: 10: main( int argc, char *argv[] ) 11: { 12: char *pattern ; 13: char *filename ; 14: if( argc != 3 ) 15: { 16: fprintf(stderr,"Usage: %% %s pattern file\n",argv[0]); 17: exit( 1 ); 18: } 19: pattern = argv[1] ; 20: filename = argv[2] ; 21: printf("pattern==%s, file == %s \n", pattern, filename ); 22: } ----------------------------------------------------------------------
rot13 や rot-N などの文字単位のフィルタは、例題の
filter-char.c
の大部分をそのまま使い
toupper() だけを置き換えても実現できる。
file-copy.c, stdio-thru.c の2つのプログラムを参考にして、UNIX の tee プログラムと同じような機能をもつプログラムを作りなさい。(tee コマンド について詳しくは、man tee を見なさい。)プログラムの名前は、tee ではな く、別の名前(mytee) としなさい。tee の場合、標準の tee が動いているの か、自分で作った tee が動いているのか区別がつかない。ただし、出力する ファイルは、一つでもよい。-i オプションや -a オプションには対応しなく てもよい。
mytee は、次のようにして、標準入力を標準出力にコピーしながら、同時にファ イルにも保存するものである。
この結果として、画面には、次のように grep コマンドを実行した時と同じ結果が表示される。% grep pattern file1 | ./mytee result![]()
% grep pattern file1![]()
同時に、ファイル reult には、画面に出力された結果とまったく同じものが 保存される。
tee コマンドの名前は、アルファベットの「T」に由来する。図形的に考えれ ば、動きを理解することができる。tee コマンドは、左(パイプラインで標準 入力)から入ったデータを下(引数で与えられたファイル)に保存しながら、 右(標準出力)にも出力する。tee コマンドは、時間のかかる処理の結果や、 後で参照したい中間結果をファイルに保存するために利用される。次の例は、 makeコマンドの実行結果を make.out へ保存すると同時に、それを user にメー ルで送るものである。
% make |& tee make.out | mail -s "make result" user![]()
これにより、user は、メールが届いたことで、make コマンドの実行終了を知 ることができる。同時に、作業していたディレクトリでその結果を参照するこ とができる。
この課題では、入出力には、システムコール (open(),read(),write(),close()))を使いなさい。
UNIXの cat コマンドと同様の動きをするプログラムを作りなさい。このプロ グラムの名前を mycat とする。引数として、なにも与えられなかった時には、 標準入力を標準出力にコピーしなさい。また、引数として "-" が指定された 時には、その位置で標準入力を標準出力にコピーしなさい。たとえば、次のよ うな場合、file1 とfile2 の内容の間に、標準入力の内容をコピーしなさい。
% mycat file1 - file2![]()
この課題では、入出力には、システムコール (open(),read(),write(),close()))を使いなさい。
Linux の場合、ライブラリ関数のソース・プログラムは、探せば見つかる。こ の課題では、それを利用してもよい。ただし、それらのプログラムは、他のラ イブラリ関数を呼んでいるものがある。従って、他のライブラリ関数を呼び出 さないように、人手でインライン展開(別の関数の本体を呼び出し側に書く) しなさい。
string ライブラリの中で、strcpy(), strcat(), strlen() 以外の関数を使う プログラムを書きなさい。