C言語学習記録5 fgets使用時の入力ストリームのバッファ
fgets
char *fgets(char *s, int size, FILE *stream);
fgets は stream が示すファイルから改行('\n')または n-1 バイト目まで文字を読み込み、引数で渡された s に格納する。この n-1 バイトの中には改行自体も含まれる。その後文字列の末尾に終端記号 '\0' を付与する
入力に使用したテキストファイル
下記のようなテキストを使用する
abcdefghijklmnopqrstuvwxyz1234567890 aiueokakikukekosashisusesotachitsuteto
fgetsで2回入力してみる
下記のプログラムで入力する
fgetsの最大入力可能文字数は19文字とし、入力テキストの一行の文字数より少ない
#include<stdio.h> #include<stdlib.h> //exit() int main(void) { char file_name[] = "myfile.txt"; char buf[20]; int i; FILE *fp; if ((fp = fopen(file_name, "rb")) == NULL) { perror(file_name); return 1; } printf("一回目\n"); if (fgets(buf, sizeof(buf), fp) == NULL) { printf("入力エラー\n"); exit(1); } for (i=0;i<sizeof(buf);i++) { printf(" buf[%d] -> %c : %d\n", i, buf[i], buf[i]); } if (fgets(buf, sizeof(buf), fp) == NULL) { printf("入力エラー\n"); exit(1); } printf("二回目\n"); for (i=0;i<sizeof(buf);i++) { printf(" buf[%d] -> %c : %d\n", i, buf[i], buf[i]); } if (fclose(fp) == EOF) { perror(file_name); return 1; } return 0; }
実行例
一回目 buf[0] -> a : 97 buf[1] -> b : 98 buf[2] -> c : 99 buf[3] -> d : 100 buf[4] -> e : 101 buf[5] -> f : 102 buf[6] -> g : 103 buf[7] -> h : 104 buf[8] -> i : 105 buf[9] -> j : 106 buf[10] -> k : 107 buf[11] -> l : 108 buf[12] -> m : 109 buf[13] -> n : 110 buf[14] -> o : 111 buf[15] -> p : 112 buf[16] -> q : 113 buf[17] -> r : 114 buf[18] -> s : 115 buf[19] -> : 0 二回目 buf[0] -> t : 116 buf[1] -> u : 117 buf[2] -> v : 118 buf[3] -> w : 119 buf[4] -> x : 120 buf[5] -> y : 121 buf[6] -> z : 122 buf[7] -> 1 : 49 buf[8] -> 2 : 50 buf[9] -> 3 : 51 buf[10] -> 4 : 52 buf[11] -> 5 : 53 buf[12] -> 6 : 54 buf[13] -> 7 : 55 buf[14] -> 8 : 56 buf[15] -> 9 : 57 buf[16] -> 0 : 48 buf[17] -> : 10 buf[18] -> : 0 buf[19] -> : 0
二回目の入力では一回目の入力で読みきれなかったストリームバッファに残っている分が出力する
回避策1: オーバーした分は読み飛ばす
改行文字が含まれていない場合はwhile(fgetc(fp) != '\n');で空読みする
#include<stdio.h> #include<stdlib.h> //exit() #include<string.h> // strchr(), strlen() int main(void) { char file_name[] = "myfile.txt"; char buf[20]; int i; FILE *fp; if ((fp = fopen(file_name, "rb")) == NULL) { perror(file_name); return 1; } printf("一回目\n"); if (fgets(buf, sizeof(buf), fp) == NULL) { printf("入力エラー\n"); exit(1); } for (i=0;i<sizeof(buf);i++) { printf(" buf[%d] -> %c : %d\n", i, buf[i], buf[i]); } if (strchr(buf, '\n') != NULL) { buf[strlen(buf) - 1] = '\0'; } else { // 改行文字がない場合は、bufのサイズを超えた場合のため、 // 入力ストリームを空読みする while(fgetc(fp) != '\n'); } if (fgets(buf, sizeof(buf), fp) == NULL) { printf("入力エラー\n"); exit(1); } printf("二回目\n"); for (i=0;i<sizeof(buf);i++) { printf(" buf[%d] -> %c : %d\n", i, buf[i], buf[i]); } if (fclose(fp) == EOF) { perror(file_name); return 1; } return 0; }
実行例
一回目 buf[0] -> a : 97 buf[1] -> b : 98 buf[2] -> c : 99 buf[3] -> d : 100 buf[4] -> e : 101 buf[5] -> f : 102 buf[6] -> g : 103 buf[7] -> h : 104 buf[8] -> i : 105 buf[9] -> j : 106 buf[10] -> k : 107 buf[11] -> l : 108 buf[12] -> m : 109 buf[13] -> n : 110 buf[14] -> o : 111 buf[15] -> p : 112 buf[16] -> q : 113 buf[17] -> r : 114 buf[18] -> s : 115 buf[19] -> : 0 二回目 buf[0] -> a : 97 buf[1] -> i : 105 buf[2] -> u : 117 buf[3] -> e : 101 buf[4] -> o : 111 buf[5] -> k : 107 buf[6] -> a : 97 buf[7] -> k : 107 buf[8] -> i : 105 buf[9] -> k : 107 buf[10] -> u : 117 buf[11] -> k : 107 buf[12] -> e : 101 buf[13] -> k : 107 buf[14] -> o : 111 buf[15] -> s : 115 buf[16] -> a : 97 buf[17] -> s : 115 buf[18] -> h : 104 buf[19] -> : 0
二回目ではテキストの二行目が読み込まれた
回避策2: オーバーした場合はエラーにする
下記のようにエラーにする
if (strchr(buf, '\n') != NULL) { buf[strlen(buf) - 1] = '\0'; } else { printf("一行に入力可能な最大文字数(%d文字)をオーバーしました\n", sizeof(buf) -1 ); exit(1); }
実行例
一回目 buf[0] -> a : 97 buf[1] -> b : 98 buf[2] -> c : 99 buf[3] -> d : 100 buf[4] -> e : 101 buf[5] -> f : 102 buf[6] -> g : 103 buf[7] -> h : 104 buf[8] -> i : 105 buf[9] -> j : 106 buf[10] -> k : 107 buf[11] -> l : 108 buf[12] -> m : 109 buf[13] -> n : 110 buf[14] -> o : 111 buf[15] -> p : 112 buf[16] -> q : 113 buf[17] -> r : 114 buf[18] -> s : 115 buf[19] -> : 0 一行に入力可能な最大文字数(19文字)をオーバーしました
C言語学習記録4 fgetsを使った標準入力
ファイルを削除するプログラム
fgetsは末尾の改行も保存するのでstrchrで検索し削除を行う
(標準入力やテキストファイルから入力した場合に末尾に改行が入る)
#include<stdio.h> #include<stdlib.h> // exit() #include<ctype.h> // toupper() #include<string.h> // strchr(), strlen() int main(void) { char buf[80]; printf("削除するファイルの名前を入力してください: "); // fgetsはstream の先頭が EOF だった場合もしくは途中でエラーが起こった場合は NULL // sizeof(buf) は80 // ただしfgetsは第二引数で渡された数値 -1までを読み込むので79文字 if (fgets(buf, sizeof(buf), stdin) == NULL) { fprintf(stderr, "入力エラー\n"); exit(1); } //末尾の改行を削除する if (strchr(buf, '\n') != NULL) { buf[strlen(buf) - 1] = '\0'; } else { // 改行文字がない場合は、bufのサイズを超えた場合のためエラー終了する fprintf(stderr, "一行に入力可能な最大文字数(%d文字)をオーバーしました\n", sizeof(buf) -1 ); exit(1); } printf("削除してもよろしいですか (Y/N) "); if (toupper(getchar()) == 'Y') { if (remove(buf)) { // removeに失敗した場合はエラー終了 perror(buf); exit(1); } } else { printf("入力エラー\n"); exit(1); } return 0; }
実行例(正常に削除した場合)
$ ./file_rm 削除するファイルの名前を入力してください: file.txt 削除してもよろしいですか (Y/N) y
実行例(存在しないファイルを削除しようとした場合)
$ ./file_rm 削除するファイルの名前を入力してください: aiueo 削除してもよろしいですか (Y/N) y aiueo: No such file or directory
odコマンドの使い方
cat file.txt
下記のファイルでodを使用
aiueo
1バイトずつ10進数で表示
od -td1 file.txt
結果
0000000 97 105 117 101 111 10 0000006
C言語学習記録3 fseek ftellを使ったファイル入力
ファイルを表示
#include<stdio.h> int main(void) { FILE *fp; char file_name[] = "test_file"; long i, loc; char ch; if ((fp = fopen(file_name, "rb")) == NULL) { perror(file_name); return 1; } fseek(fp, 0L, SEEK_END); loc = ftell(fp); loc = loc - 1; for (i=0;i<=loc;i++) { fseek(fp, i, SEEK_SET); ch = fgetc(fp); printf("%c", ch); } if (fclose(fp) == EOF) { perror(file_name); return 1; } return 0; }
C言語学習記録2 fread fwriteを使ったファイル入出力
fread
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
fread() 関数は stream ポインタで指定されたストリームから nmemb 個のデータを読み込み、 ptr で与えられた場所に格納する。個々のデータは size バイトの長さを持つ。
fwrite
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
fwrite() 関数は ptr で指定された場所から得た nmemb 個のデータを、 stream ポインタで指定されたストリームに書き込む。個々のデータは size バイトの長さを持つ。
ランダムに生成した100個の整数をfwriteを使って書き込み
#include<stdio.h> #include<stdlib.h> // rand(), exit() int main(void) { int num, i; char *file_name = "rand"; FILE *fp; if ((fp = fopen(file_name, "wb")) == NULL) { perror(file_name); exit(1); } for (i=0;i<100;i++){ num = rand(); if (fwrite(&num, sizeof(num), 1, fp) != 1) { perror(file_name); exit(1); } } if (fclose(fp) == EOF) { perror(file_name); exit(1); } return 0; }
上記で作成したファイルをfreadを使って読み込み
#include<stdio.h> #include<stdlib.h> // rand(), exit() int main(void) { int num, i; char *file_name = "rand"; FILE *fp; if ((fp = fopen(file_name, "rb")) == NULL) { perror(file_name); exit(1); } for (i=0;i<100;i++){ num = rand(); if (fread(&num, sizeof(num), 1, fp) != 1) { perror(file_name); exit(1); } printf("%d ", num); } if (fclose(fp) == EOF) { perror(file_name); exit(1); } return 0; }
実行例
1804289383 846930886 1681692777 1714636915 (以下省略)
odコマンドで確認した結果とも一致する
$ od -td4 rand 0000000 1804289383 846930886 1681692777 1714636915 (以下省略)
fseekを使って指定した順番の数字を表示
#include<stdio.h> #include<stdlib.h> // exit() #include<string.h> // strchr(), strlen() int main(void) { int num; long i; char *file_name = "rand"; char buf[80]; FILE *fp; if ((fp = fopen(file_name, "rb")) == NULL) { perror(file_name); exit(1); } printf("何番目の数字を表示したいですか?: "); if (fgets(buf, sizeof(buf), stdin) == NULL) { printf("入力エラー\n"); exit(1); } if (strchr(buf, '\n') != NULL) { buf[strlen(buf) - 1] = '\0'; } else { fprintf(stderr, "一行に入力可能な最大文字数(%d文字)をオーバーしました\n", sizeof(buf) -1 ); exit(1); } i = atoi(buf) - 1; if (fseek(fp, i * sizeof(int), SEEK_SET)) { fprintf(stderr, "シークエラー\n"); exit(1); } if (fread(&num, sizeof(num), 1, fp) != 1) { perror(file_name); exit(1); } printf("%d ", num); if (fclose(fp) == EOF) { perror(file_name); exit(1); } return 0; }
実行例
何番目の数字を表示したいですか?: 100 1956297539
C言語学習記録1 fgetc fputcを使ったファイル入出力
ファイルオープン
char file_name[] = "myfile.txt"; FILE *fp; if ((fp = fopen(file_name, "rb")) == NULL) { perror(file_name); return 1; }
オープンモード
オープンモード | 意味 | ファイルがあるとき | ファイルがないとき |
---|---|---|---|
r | 読み出し専用 | 正常 | エラー(NULL返却) |
w | 書き込み専用 | サイズを 0 にする(上書き) | 新規作成 |
a | 追加書き込み専用 | 最後に追加する | 新規作成 |
r+ | 読み込みと書き込み | 正常 | エラー(NULL返却) |
w+ | 書き込みと読み込み | サイズを 0 にする(上書き) | 新規作成 |
a+ | 読み込みと追加書き込み | 最後に追加する | 新規作成 |
上記のテキストモードは環境依存の物理的な改行コードと '\n' を相互変換する
変換したくない場合はrb, wb, ab, r+b, w+b, a+bのバイナリモードを使用する
例外処理
fopenはファイルオープンに失敗した場合、戻り値としてNULLが返されます。ですので、ファイルオープンが失敗したことを通知してプログラムを終了させる必要があります。
ファイル読み込み
char c; while (1) { c = fgetc(fp); if (ferror(fp)) { perror(file_name); fclose(fp); return 1; } else if (feof(fp)) { break; } }
例外処理
fgetc()はエラーが起こったかファイルの終わりに達した場合EOF(-1)を返す
どちらが起こったか判定するためにferror()関数とfeof()関数を使う
ferror()
エラーが起こって入れば0以外を返す
feof()
ファイルの終わりに達していれば0以外を返す
ファイルクローズ
if (fclose(fp) == EOF) { perror(file_name); return 1; }
例外処理
fclose()は成功すると0を返し、エラーが起こるとEOF(-1)を返す
ファイルへの書き込み
書き込みモードwまたはwbでファイルをオープンします
FILE *fp; char file_name[] = "myfile.txt"; if ((fp = fopen(file_name, "wb")) == NULL) { perror(file_name); return 1; }
ファイルへ書き込みにはfputc関数を使用します
char c; c = 'a'; fputc(c, fp); if (ferror(fp)) { perror(file_name); fclose(fp); return 1; }
書き込みが終わったらファイルをクローズします
if (fclose(fp) == EOF) { perror(file_name); return 1; }
ファイルの入れ替えを行うプログラム
#include<stdio.h> #include<stdlib.h> int file_copy(char *file1, char *file2); int main(int argc, char *argv[]) { char tmp_file[] = "/var/tmp/tmp_file.txt"; if (argc != 3) { printf("<プログラム名> <ファイル1> <ファイル2>\n"); exit(1); } if (file_copy(argv[1], tmp_file)) { return 1; } if (file_copy(argv[2], argv[1])) { return 1; } if (file_copy(tmp_file, argv[2])) { return 1; } return 0; } int file_copy(char *file1, char *file2) { FILE *fp1, *fp2; char c; if ((fp1 = fopen(file1, "rb")) == NULL) { perror(file1); return 1; } if ((fp2 = fopen(file2, "wb")) == NULL) { perror(file2); fclose(fp1); return 1; } while (1) { c = fgetc(fp1); if (ferror(fp1)) { perror(file1); fclose(fp1); fclose(fp2); return 1; } else if (feof(fp1)) { break; } fputc(c, fp2); if (ferror(fp2)) { perror(file2); fclose(fp1); fclose(fp2); return 1; } } if (fclose(fp1) == EOF) { perror(file1); fclose(fp2); return 1; } if (fclose(fp2) == EOF) { perror(file2); return 1; } return 0; }
実行結果
python学習記録8 リストの内包表現
今日も下記のサイトで学習しました。
Python 早めぐり
http://www.shido.info/py/python2.html
今日は「4.2.4. リストの内包表現」を学習しました
上記のサイトだけだとちょっとよく理解できなかったので下記のサイトも見てみました
Pythonの技法:リストの内包表記
http://builder.japan.zdnet.com/news/story/0,3800079086,20364671,00.htm
学習したこと
リストに何らかの処理をしてリストを生成するときは内包表現を使って書くことが出来る
内包表記を利用すると、より使用が簡単で可読性の高いコードを記述できる。
wordlistの文字を全て小文字にする場合
内包表記を使わない場合
#!/usr/bin/env python wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?'] ls=[] for word in wordlist: ls.append(word.upper()) print ls
内包表記を使った場合
#!/usr/bin/env python wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?'] print [word.lower() for word in wordlist]
実行結果
['HELLO', 'WORLD', 'HOW', 'ARE', 'YOU?']
wordlistの文字のうち全て小文字のものを大文字にする場合
内包表記を使わない場合
#!/usr/bin/env python wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?'] ls=[] for word in wordlist: if word.islower(): ls.append(word.upper()) print ls
内包表記を使った場合
#!/usr/bin/env python wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?'] print [word.lower() for word in wordlist if word.islower()]
実行結果
['HOW']
wordlistからvowels以外の文字を抜き出す場合
内包表記を使わない場合
#!/usr/bin/env python wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?'] vowels = ['a','A','e','E','i','I','o','O','u','U'] ls1=[] for word in wordlist: ls2=[] for letter in word: if letter not in vowels: ls2.append(letter) ls1.append(ls2) print ls1
内包表記を使った場合
#!/usr/bin/env python wordlist = ['HELLO', 'World', 'how', 'aRe', 'YOU?'] vowels = ['a','A','e','E','i','I','o','O','u','U'] print [[letter for letter in word if letter not in vowels] for word in wordlist]
実行結果
[['H', 'L', 'L'], ['W', 'r', 'l', 'd'], ['h', 'w'], ['R'], ['Y', '?']]