int main()
{
FILE *ft;
char ch;
ft=fopen("abc.txt","r+");
if(ft==NULL)
{
printf("can not open target file\n");
exit(1);
}
while(1)
{
ch=fgetc(ft);
if(ch==EOF)
{
printf("done");
break;
}
if(ch=='i')
{
fputc('a',ft);
}
}
fclose(ft);
return 0;
}
i
がa
に置き換えられるようにabc.txt
を編集したいことがわかります。
プログラムは正常に動作しますが、外部でabc.txt
を開くと、編集されていないように見えました。
その理由として考えられるものはありますか?
この場合、回答が示すように、i
の後の文字がa
に置き換えられないのはなぜですか?
複数の問題があります:
fgetc()
は、int
ではなくchar
を返します。すべての有効なchar
値に加えて個別の値EOFを返す必要があります。書かれているように、EOFを確実に検出することはできません。 char
が符号なし型の場合、EOFは見つかりません。 char
が符号付きタイプの場合、有効な文字(多くの場合、ÿ、y-umlaut、U + 00FF、分音付きのラテン文字Y)をEOFと誤認します。
更新モードで開いたファイルの入力と出力を切り替える場合は、読み取りの間にファイル配置操作(fseek()
、rewind()
、名目上fsetpos()
)を使用する必要があります。と書く;また、書き込みと読み取りの間に位置決め操作またはfflush()
を使用する必要があります。
開いたものを閉じることをお勧めします(現在はコードで修正されています)。
書き込みが機能した場合は、i
の後の文字をa
で上書きします。
これらの変更により、次のことが起こります。
_#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *ft;
char const *name = "abc.txt";
int ch;
ft = fopen(name, "r+");
if (ft == NULL)
{
fprintf(stderr, "cannot open target file %s\n", name);
exit(1);
}
while ((ch = fgetc(ft)) != EOF)
{
if (ch == 'i')
{
fseek(ft, -1, SEEK_CUR);
fputc('a',ft);
fseek(ft, 0, SEEK_CUR);
}
}
fclose(ft);
return 0;
}
_
さらにエラーチェックの余地があります。
fseek(ft, 0, SEEK_CUR);
ステートメントは、C標準で必要です。
ISO/IEC 9899:2011§7.21.5.3
fopen
関数¶7ファイルが更新モード(上記のモード引数値のリストの2番目または3番目の文字として「+」)で開かれると、入力と出力の両方が関連するストリームで実行される場合があります。 ただし、
fflush
関数またはファイル配置関数(fseek
、fsetpos
、またはrewind
)への呼び出しが介在しない限り、出力の直後に入力が続くことはありません。入力操作でファイルの終わりが検出されない限り、ファイル配置関数への介在呼び出し。更新モードでテキストファイルを開く(または作成する)と、一部の実装では代わりにバイナリストリームを開く(または作成する)場合があります。
(強調が追加されました。)
fgetc()
はint
を返します現在のC標準であるISO/IEC 9899:2011からの引用。
§7.21入力/出力_
<stdio.h>
_§7.21.1はじめに
EOF
は、int型と負の値を持つ整数定数式に展開され、ファイルの終わり、つまりストリームからの入力がなくなったことを示すためにいくつかの関数によって返されます。§7.21.7.1
fgetc
関数
int fgetc(FILE *stream);
¶2streamが指す入力ストリームのファイルの終わりインジケーターが設定されておらず、次の文字が存在する場合、
fgetc
関数はその文字を_unsigned char
_としてint
に変換して取得し、関連するファイルを進めますストリームの位置インジケーター(定義されている場合)。返品
¶3ストリームのファイルの終わりインジケーターが設定されている場合、またはストリームがファイルの終わりにある場合、ストリームのファイルの終わりインジケーターが設定され、
fgetc
関数はEOFを返します。それ以外の場合、fgetc
関数は、streamが指す入力ストリームから次の文字を返します。読み取りエラーが発生すると、ストリームのエラーインジケーターが設定され、fgetc
関数がEOFを返します。289)289) ファイルの終わりと読み取りエラーは、
feof
関数とferror
関数を使用して区別できます。
したがって、EOF
は負の整数です(通常は-1ですが、標準ではそれは必要ありません)。 fgetc()
関数は、EOFまたは文字の値を_unsigned char
_(0..UCHAR_MAXの範囲、通常は0..255)として返します。
§6.2.5タイプ
¶3タイプ
char
として宣言されたオブジェクトは、基本的な実行文字セットのメンバーを格納するのに十分な大きさです。基本実行文字セットのメンバーがchar
オブジェクトに格納されている場合、その値は負でないことが保証されます。他の文字がchar
オブジェクトに格納されている場合、結果の値は実装によって定義されますが、そのタイプで表現できる値の範囲内でなければなりません。¶5タイプ_
signed char
_として宣言されたオブジェクトは、「プレーン」なchar
オブジェクトと同じ量のストレージを占有します。§6符号付き整数型ごとに、同じ量のストレージ(符号情報を含む)を使用し、同じ配置要件を持つ、対応する(ただし異なる)符号なし整数型(キーワード
unsigned
で指定)があります。§153つのタイプ
char
、_signed char
_、および_unsigned char
_は、まとめて文字タイプと呼ばれます。実装は、_signed char
_または_unsigned char
_と同じ範囲、表現、および動作を持つようにchar
を定義する必要があります。45)45) _
CHAR_MIN
_で定義された_<limits.h>
_は、値_0
_または_SCHAR_MIN
_のいずれかを持ち、これを使用して2つのオプションを区別できます。選択に関係なく、char
は他の2つのタイプとは別のタイプであり、どちらとも互換性がありません。
これは、プレーンなchar
が符号付きまたは符号なしの型である可能性があるという私の主張を正当化します。
今考えてみましょう:
_char c = fgetc(fp);
if (c == EOF)
…
_
fgetc()
がEOFを返し、プレーンchar
が符号なし(8ビット)タイプであり、EOF is _-1
_であるとします。割り当てにより、値0xFFがc
に配置されます。 、これは正の整数です。比較が行われると、c
はint
(したがって、値255)にプロモートされ、255は負ではないため、比較は失敗します。
逆に、プレーンchar
が符号付き(8ビット)タイプであり、文字セットがISO8859-15であると仮定します。 fgetc()
がÿを返す場合、割り当てられる値はビットパターン0b11111111になります。これは、_-1
_と同じであるため、比較では、c
は_-1
_に変換され、比較されます。 _c == EOF
_は、有効な文字が読み取られた場合でもtrueを返します。
詳細を微調整することはできますが、基本的な引数はsizeof(char) < sizeof(int)
の間有効です。それが当てはまらないDSPチップがあります。ルールを再考する必要があります。それでも、基本的なポイントは残ります。 fgetc()
は、int
ではなくchar
を返します。
データが本当にASCII(7ビットデータ)の場合、すべての文字は0..127の範囲にあり、ÿ問題の誤解に遭遇することはありません。ただし、 char
タイプは署名されていませんが、「EOFを検出できません」という問題がまだ残っているため、プログラムは長時間実行されます。移植性を考慮する必要がある場合は、これを考慮に入れます。これらは、必要なプロフェッショナルグレードの問題です。 Cプログラマーとして扱います。これらすべてのニュアンスを考慮せずに、比較的簡単にデータのシステムで動作するプログラムにたどり着くことができます。ただし、プログラムは他の人のシステムでは動作しません。
_abc.txt
_の「i」を変更するのではなく、「i」の次の文字を変更します。 fseek(ft, -1, SEEK_CUR);
の前にfputc('a', ft);
を配置してみてください。
'i'文字を読み取った後、ft
のファイル位置インジケータはこの 'i'の後の文字になり、fputc()
で文字を書き込むと、この文字は書き込まれます。現在のファイル位置、つまり「i」の後の文字。詳細については、 fseek(3)
を参照してください。
'i'を読んだ後、正しい場所に書き込むために「ステップバック」する必要があります。
if(ch=='i')
{
fseek(ft, -1, SEEK_CUR);
fputc('a',ft);
}