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

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', '?']]