次のコードを使用して行を読み込もうとしています。
while(fscanf(f, "%[^\n\r]s", cLine) != EOF )
{
/* do something with cLine */
}
しかし、どういうわけか私は毎回最初の行しか取得しません。これは行を読むのに悪い方法ですか?期待どおりに機能させるには、何を修正する必要がありますか?
この関数を使用すると、失敗したときにファイルポインタが不明な場所に残る可能性があるため、ほとんどの場合、この関数を使用することはお勧めできません。fscanf()
fgets()
私は、各行を入力してからそれを取得するために使用することを好みsscanf()
ます。その後、適切と思われる場合は、読み込まれた行を引き続き調べます。何かのようなもの:
#define LINESZ 1024
char buff[LINESZ];
FILE *fin = fopen ("infile.txt", "r");
if (fin != NULL) {
while (fgets (buff, LINESZ, fin)) {
/* Process buff here. */
}
fclose (fin);
}
fgets()
改行文字に遭遇するまで文字列を読んで、あなたがやろうとしていることのようです。
ファイルを 1 行ずつ読みたい場合 (ここでは、行区切り記号 == '\n')、次のようにします。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *fp;
char *buffer;
int ret;
// Open a file ("test.txt")
if ((fp = fopen("test.txt", "r")) == NULL) {
fprintf(stdout, "Error: Can't open file !\n");
return -1;
}
// Alloc buffer size (Set your max line size)
buffer = malloc(sizeof(char) * 4096);
while(!feof(fp))
{
// Clean buffer
memset(buffer, 0, 4096);
// Read a line
ret = fscanf(fp, "%4095[^\n]\n", buffer);
if (ret != EOF) {
// Print line
fprintf(stdout, "%s\n", buffer);
}
}
// Free buffer
free(buffer);
// Close file
fclose(fp);
return 0;
}
楽しみ :)
fscanf を使用してファイルを読み取り/トークン化すると、常に壊れやすいコードまたは痛みと苦しみが生じます。行を読み取り、その行をトークン化またはスキャンすることは安全で効果的です。より多くのコード行が必要です。つまり、何をしたいのかを考えるのに時間がかかります (そして有限の入力バッファー サイズを処理する必要があります)。
fscanf と戦わないでください。使用しないでください。これまで。
試してみるとwhile( fscanf( f, "%27[^\n\r]", cLine ) == 1 )
、もう少し運がいいかもしれません。オリジナルからの 3 つの変更点:
27
ここでは例として使用しましたが、残念ながら、scanf()
ファミリはフォーマット文字列で文字通りフィールド幅を必要とし、値を渡すことができる*
メカニズムを使用できませんprintf()
s
フォーマット文字列の the を取り除く- %[
「セットに一致する、または一致しないすべての文字」のフォーマット指定子であり、セットはそれ自体で a]
で終了しますfgets()
とは言うものの、 を使用してバッファに収まる限り多くの行を読み込むことで、痛みを軽減して同じ結果を得ることができます。
fscanf文字列で正規表現演算子を使用しようとしているように見えます。この文字列 [^\n\r]
はfscanfにとって何の意味もありません。そのため、コードは期待どおりに機能しません。
さらに、アイテムが一致しない場合、fscanf()はEOFを返しません。むしろ、一致の数を示す整数を返します。この場合、おそらくゼロです。EOFは、ストリームの最後またはエラーの場合にのみ返されます。したがって、この場合に起こっていることは、fscanf()の最初の呼び出しがファイルの最後まで読み取り、一致する文字列を探してから0を返し、一致するものが見つからなかったことを通知することです。次に、ファイル全体が読み取られたため、2番目の呼び出しはEOFを返します。
最後に、%s scanf形式の演算子は、次の空白文字にのみキャプチャするため、どのような場合でも\nまたは\rを除外する必要がないことに注意してください。
詳細については、fscanfのドキュメントを参照してください:http ://www.cplusplus.com/reference/clibrary/cstdio/fscanf/
ループにはいくつかの問題があります。あなたが書いた:
while( fscanf( f, "%[^\n\r]s", cLine ) != EOF )
/* do something */;
考慮すべき事項:
fscanf() は、保存されているアイテムの数を返します。ファイルの終わりを超えて読み取る場合、またはファイル ハンドルにエラーがある場合は、EOF を返すことができます。cLine
バッファに新しいコンテンツがなく、正常に読み取られたゼロの有効な戻り値を区別する必要があります。
ファイル ハンドルが現在ストリーム内のどこを指しているかを予測するのが難しいため、一致に失敗すると問題が発生します。これにより、失敗したマッチからの回復が予想以上に難しくなります。
あなたが書いたパターンは、おそらくあなたが意図したものではありません。CR または LF ではない任意の数の文字に一致し、リテラルが見つかることを期待していますs
。
バッファをオーバーフローから保護していません。バッファに割り当てられたサイズに関係なく、任意の数の文字をファイルから読み取ってバッファに書き込むことができます。これは残念ながら一般的なエラーであり、多くの場合、攻撃者が選択した任意のコードを実行するために攻撃者によって悪用される可能性があります。
バイナリ モードで開くことを特に要求しない限りf
、行末の変換はライブラリで行われ、通常は CR 文字が表示されることはなく、通常はテキスト ファイルでも表示されません。
おそらく、次のようなループが必要になるでしょう。
while(fgets(cLine, N_CLINE, f)) {
/* do something */ ;
}
ここで、N_CLINE は、a を開始するバッファーで使用可能なバイト数ですcLine
。
このfgets()
関数は、ファイルから行を読み取るための非常に好ましい方法です。その 2 番目のパラメーターはバッファーのサイズであり、ファイルからバッファーにそのサイズよりも 1 小さいバイトまで読み取ります。nul
他の C 文字列関数に安全に渡すことができるように、常に文字でバッファーを終了します。
ファイルの終わり、改行、またはbuffer_size-1
読み取られたバイトの最初で停止します。
バッファに改行文字が残るため、バッファより長い単一行とバッファより短い行を区別できます。
ファイルの終わりまたはエラーのためにバイトがコピーされなかった場合は NULL を返し、それ以外の場合はバッファへのポインタを返します。これらのケースを使用feof()
したりferror()
、区別したりすることができます。