我正在尝试使用unicode流读取一个UTF8编码的文本文件。这很好,但fseek
似乎有一个错误,用以下简单的文件和程序演示:
我正在读取的文本文件:
ABC
文件的原始内容
EF BB BF 41 42 43 0D 0A
正如您所看到的,该文件包含UTF-8 BOM和字符ABC,后面跟一行的末尾。
该程序使用UNICODE支持打开文件,然后读取一行并显示缓冲区的原始内容,这是预期的。然后它搜索到开头并再次读取该行,但这次缓冲区的内容不同;在缓冲区的开头有两个字节,它们实际上是UTF-16小端编码文件的BOM。
程序
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *input = _wfopen(L"utf8filewithbom.txt", L"r, ccs=UTF-8");
if (input == NULL)
{
printf("Can't open filen");
return 1;
}
unsigned char buffer[100];
fgetws((wchar_t*)buffer, _countof(buffer) / 2, input);
printf("First 4 bytes of buffer: %02x %02x %02x %02xn", buffer[0], buffer[1], buffer[2], buffer[3]);
fseek(input, 0, SEEK_SET);
fgetws((wchar_t*)buffer, _countof(buffer) / 2, input);
printf("First 4 bytes of buffer: %02x %02x %02x %02xn", buffer[0], buffer[1], buffer[2], buffer[3]);
fclose(input);
}
预期输出:
First 4 bytes of buffer: 41 00 42 00
First 4 bytes of buffer: 41 00 42 00
实际输出:
First 4 bytes of buffer: 41 00 42 00
First 4 bytes of buffer: ff fe 41 00
这是Microsoft CRT中的错误还是我做错了什么?
我正在使用Visual Studio 2019 16.4.3。
我尝试过但没有改变的事情:
- 使用
"rt, ccs=UTF-8"
而不是"r, ccs=UTF-8"
- 读取UTF-16 little-endian编码的文件,而不是UTF-8编码的文件
根据fseek
:上的Microsoft文档
CRT打开以字节顺序标记(BOM(开头的文件时,文件指针位于BOM之后(即文件实际内容的开头(。如果必须将
fseek
放在文件的开头,请使用ftell
获取初始位置,并使用fseek
获取初始位置而不是位置0。
基本上,只需将代码调整为(更改/添加行的注释(:
FILE *input = _wfopen(L"utf8filewithbom.txt", L"r, ccs=UTF-8");
if (input == NULL)
{
printf("Can't open filen");
return 1;
}
const long postbomoffset = ftell(input); // Store post-BOM offset
unsigned char buffer[100];
fgetws((wchar_t*)buffer, _countof(buffer) / 2, input);
printf("First 4 bytes of buffer: %02x %02x %02x %02xn", buffer[0], buffer[1], buffer[2], buffer[3]);
fseek(input, postbomoffset, SEEK_SET); // Seek to post-BOM offset, not raw beginning
fgetws((wchar_t*)buffer, _countof(buffer) / 2, input);
printf("First 4 bytes of buffer: %02x %02x %02x %02xn", buffer[0], buffer[1], buffer[2], buffer[3]);
fclose(input);