在 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);
但是,在调试程序时,第一个ftell()
返回的位置值是 1 而不是 2...所以当_fgetts()
第二个文本行被调用时,它只会得到一个 CR 标记而不是 character 2
。
我想知道在文本模式下处理文件是否能力不足"r,ccs=UTF-8"
(示例在"r"
模式下运行良好(编辑:不是真的!第一个 ftell() 返回 0。感谢 Hans 指出))。
(更奇怪的是,ftell()
当 UTF-8 文本文件包含任何非 ANSI 字符时,它可以正常工作......但让我们先解决纯 ANSI 文件。是的,我已经在论坛中搜索过,但令人惊讶的是没有找到类似的提问者)
到目前为止,最好的解决方法是在"r"
模式下读取字符串行,然后将它们从 UTF-8 编码转换为 Unicode 编码。任何更熟练的建议将不胜感激。
----- 更新分隔符 (2015/03/25) -----
在 MinGW + Win7 和 GCC + CentOS 下测试
在收到以下关键点的宝贵意见时,
- 编译器实现:Microsoft vs GNU @nm
内部缓冲区使用不准确
ftell()
:@Hans Passant- 固定长度编码(例如“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,带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,如果我遗漏了什么,请修改结论。