VStudio 2012 + Win7 でテスト
UTF-8 テキスト ファイルには、わずか 5 バイトしか含まれていません。
31 0a 32 0a 0a
テキストモードでは、次のように表示されます。
1
2
ソースも簡単です:
FILE *fp;
TCHAR buf[100] ={0};
TCHAR *line;
LONG pos;
_tfopen_s(&fp, _T("...\\test.txt"), _T("r,ccs=UTF-8"));
line = _fgetts(buf, 100, fp);
pos = ftell(fp);
if(fseek(fp, pos, SEEK_SET)!=0)
perror( "fseek error");
line = _fgetts(buf, 100, fp);
pos = ftell(fp);
fclose(fp);
ただし、プログラムをデバッグするとき、1ftell()
番目は 2 ではなく 1 の位置値を返します...したがって、_fgetts()
2 番目のテキスト行に対して get が呼び出されると、 character ではなく CR マークが取得されるだけです2
。
テキストモードでファイルを処理するのに不備があるのではないかと思います(サンプルはモード"r,ccs=UTF-8"
でうまく機能します(編集:真ではありません!最初の ftell() は0を返します。指摘してくれてありがとうハンス))。(UTF-8テキストファイルにANSI以外の文字が含まれている場合に正しく動作するのはさらに奇妙です...しかし、最初に純粋なANSIファイルを解決しましょう。そして、はい、すでにフォーラムを検索しましたが、驚くべきことに同様の質問者は見つかりませんでした)"r"
ftell()
現在の最善の回避策は、"r"
モードで文字列行を読み取り、UTF-8 エンコーディングから Unicode エンコーディングに変換することです。より巧妙な提案があれば、本当に感謝します。
----- UPDATE 分割 (2015/03/25) -----
MinGW + Win7 および GCC + CentOS でのテスト
以下の要点について貴重なご意見を頂戴し、
- コンパイラの実装: Microsoft vs GNU @nm
@Hans Passantの不正確な内部バッファ使用
ftell()
- 固定長エンコーディング (例: "r" モード) と可変長エンコーディング (例: "r,css=UTF-8" モード)
- 1 文字の行末 (単一 LF) と 2 文字の行末 (CR+LF) @Hans Passant、@IInspectable
複合条件下で問題をテストすることにしました。
使用したテキストファイル
line-feed ANSI/mixed BOM encoding
1.txt single-LF pure n/a UTF-8
2.txt CR-LF* pure n/a UTF-8
3.txt CR-LF* mixed n/a UTF-8
4.txt CR-LF* mixed EFBBBF UTF-8
5.txt CR-LF* mixed FFFE UTF-16
* Except for tests under CentOS, which use single-LF only.
使用するソース (GNU コンパイラ用)
FILE *fp;
wchar_t buf[100] ={0};
wchar_t *line;
long pos;
//setlocale(LC_CTYPE, "en_GB.UTF-8"); //uncomment this for GNU+CentOS
fp = fopen("....txt", "r"); //or "r,ccs=UTF-8"
pos = ftell(fp);
if(fseek(fp, pos, SEEK_SET)!= 0)
perror( "fseek error" );
line = fgetws(buf, 100, fp);
pos = ftell(fp);
if(fseek(fp, pos, SEEK_SET)!= 0) //breakpoint, check result of ftell()
perror( "fseek error" );
line = fgetws(buf, 100, fp);
pos = ftell(fp);
fclose(fp);
結果 #1: "r" モード、GNU+Win7
1.txt(single LF): pos=0, NG `Really failed!(@Hans Passant, @IInspectable)
2.txt(pure ANSI): pos=7, OK
3.txt(non-ANSI): pos=13, OK(String is UTF-8 encoded)
4.txt(BOM=EFBBBF,UTF-8): pos=9, NG(BOM is also read)
5.txt(BOM=FFFE,UTF-16): pos=9, NG(BOM is also read)
結果 #2: "r,ccs=UTF-8"モード、GNU+Win7、setlocale() あり/なし
1.txt(single LF): pos=-3!, NG(1st line can be read, UTF-16="\0x31\0xa")
2.txt(pure ANSI): pos=0, NG(1st line can be read, UTF-16=L"1abcd\n")
3.txt(non-ANSI): pos=8, NG(1st line can be read, UTF-16. but 2nd line is incorrect!)
4.txt(BOM=EFBBBF,UTF-8): pos=9, OK!(BOM ignored, String is UTF-16 = "\0x31\0x4f60\0xa". 2nd line is "\0x32\0x597d")
5.txt(BOM=FFFE,UTF-16): pos=10, OK!(BOM ignored, String is UTF-16 = "\0x31\0x4f60\0xa". 2nd line is "\0x32\0x597d")
結果 #3: "r,ccs=UTF-8" モード、GNU+ CentOS、WITH setlocale()
1.txt(single LF): pos=2, OK
2.txt(pure ANSI): pos=6, OK
3.txt(non-ANSI): pos=12, OK
4.txt(BOM=EFBBBF,UTF-8): not tested
5.txt(BOM=FFFE,UTF-16): not tested
結論
- GNU+CentOS の場合、 が使用されている場合 (および
setlocale()
使用されている場合のみ)、ftell()
完全に機能します。これは、単一 LF の改行が Unix の標準であるためだと思います。 - ただし、Windows の場合、シングル LF または
"ccs=UTF-8"
モードのいずれかを使用するftell()
と、警告なしで不正確な戻り値が返されます...setlocale()
ここでは違いはありません。ただし、BOM が添付された UTF-8/UTF-16 ファイルは完全に処理できます...つまりftell()
、可変長エンコーディングを処理できる可能性があるということですか??
最後に、前に述べたように、"r"
モード (CR+LF 行末ルールへの準拠) は「世界を救う」でしょう.~
@Hans Passant、@ nm、何か見逃した場合は、結論を親切に修正してください。