技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

C言語:NUL文字を含むファイルを1レコードずつ読み込む

ググっても出てこなかったのでメモ。
 
C言語でファイルを1レコード読む場合は、通常はfgetsを使用します。
しかし、fgetsで読み込んだレコードにNUL文字(\0)が含まれる場合、読みこんだ文字列がNUL終端されてしまい、NUL文字以降改行コードまでの文字列を取得できなくなってしまいます。
NUL文字以降の文字列を取得したい場合は、fgetcで1文字ずつ読み込み、NUL文字や改行コードを読みこんだ際の処理を自力で実装する必要があります。
Linuxのコマンド等でNUL文字を他の文字に置換し中間ファイルに出力し、その中間ファイルをfgetsで読み込んでも良いのですが、余分なファイルIOが発生するので性能がシビアな要件では避けたい実装です)
 
以下、サンプルコードです。
NUL文字を含むファイルを1レコードずつ読み込み、レコードフォーマットを編集してファイルに出力する処理です。
 
【サンプルコード】
#include <stdio.h>
#include <stdlib.h>
 
#define N 1024
 
/* main */
int main(void) {
    FILE *fp1;
    FILE *fp2;
    char *filename1 = "/home/hoge/in.txt";
    char *filename2 = "/home/hoge/out.txt";
    char readline[N] = {'\0'};
    int ch;
    int i;
 
    /* 初期化 */ 
    memset(readline, '\0', sizeof(readline));
    i=0;
 
    /* ファイルのオープン */
    if ( (fp1 = fopen(filename1, "rb")) == NULL) {
        fprintf(stderr, "%sのオープンに失敗しました.\n", filename1);
        return 1;
    }
    if ( (fp2 = fopen(filename2, "wb")) == NULL) {
        fprintf(stderr, "%sのオープンに失敗しました.\n", filename2);
        return 1;
    }
 
    /* ファイルの終端まで文字を1文字ずつ読み取り出力する */
    while ( (ch = fgetc(fp1)) != EOF ) {
        /* NUL文字を変換(後の処理でNUL終端されるのを避ける) */
        if (ch==0){ //NUL文字
            ch=44;  //カンマ
        }
        /* 1文字ずつ文字列に書き込み */
        readline[i]=(char)ch;
        i++;
        /* 1レコード読みこんだ場合 */
        if (ch==10){ //改行コード(\n)
            /* 実際はここでレコードフォーマット編集 */
            /* 編集した文字列をファイル出力 */
            fprintf(fp2, "%s",readline);
            /* 次のレコード読み取りに備え初期化 */
            memset(readline, '\0', sizeof(readline));
            i=0;
        }
    }
 
    /* ファイルのクローズ */
    fclose(fp1);
    fclose(fp2);
 
    return 0;
}