(я предполагаю, что вы работаете в Windows, поскольку использование U + FEFF в качестве подписи в файлах UTF-8 в основном относится к Windows, и его просто следует избегать в других местах)
Вы можете открыть файл как файл UTF-8, а затем проверить, является ли первый символ U + FEFF. Вы можете сделать это, открыв обычный fstream на основе символов, а затем использовать wbuffer_convert, чтобы рассматривать его как последовательность блоков кода в другой кодировке. VS2010 пока не имеет большой поддержки char32_t, поэтому в следующем коде используется UTF-16 в wchar_t.
std::fstream fs(filename);
std::wbuffer_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t> wb(fs.rdbuf());
std::wistream is(&wb);
// if you don't do this on the stack remember to destroy the objects in reverse order of creation. is, then wb, then fs.
std::wistream::int_type ch = is.get();
const std::wistream::int_type ZERO_WIDTH_NO_BREAK_SPACE = 0xFEFF
if(ZERO_WIDTH_NO_BREAK_SPACE != ch)
is.putback(ch);
// now the stream can be passed around and used without worrying about the extra character in the stream.
int i;
readFromStream<int>(is,i);
Помните, что это должно быть сделано в файловом потоке в целом, а не внутри readFromFile в вашем потоке строки, потому что игнорирование U + FEFF должно выполняться только в том случае, если это самый первый символ во всем файле, если он вообще существует. Это не должно быть сделано где-либо еще.
С другой стороны, если вы довольны использованием потока на основе символов и просто хотите пропустить U + FEFF, если он есть, тогда предложение Джеймса Канзе кажется хорошим, так что вот реализация:
std::fstream fs(filename);
char a,b,c;
a = fs.get();
b = fs.get();
c = fs.get();
if (a != (char)0xEF || b != (char)0xBB || c != (char)0xBF) {
fs.seekg(0);
} else {
std::cerr << "Warning: file contains the so-called 'UTF-8 signature'\n";
}
Кроме того, если вы хотите использовать wchar_t
внутри, фасеты codecvt_utf8_utf16
и codecvt_utf8
имеют режим, который может потреблять «спецификации» для вас. Единственная проблема заключается в том, что wchar_t
широко признан бесполезным в наши дни *, и поэтому вы, вероятно, не должны этого делать.
std::wifstream fin(filename);
fin.imbue(std::locale(fin.getloc(), new std::codecvt_utf8_utf16<wchar_t, 0x10FFFF, std::consume_header));
* wchar_t
ничего не стоит, потому что указано, что он делает только одну вещь; предоставить тип данных фиксированного размера, который может представлять любую кодовую точку в символьном репертуаре локали. Он не обеспечивает общее представление между локалями (т. Е. Одно и то же значение wchar_t
может быть разными символами в разных локалях, поэтому вы не можете обязательно преобразовать в wchar_t
, переключиться на другую локаль, а затем преобразовать обратно в char
для выполнения iconv
-подобных преобразований кодирования.)
Само представление фиксированного размера ничего не стоит по двум причинам; Во-первых, многие кодовые точки имеют смысловые значения, поэтому понимание текста означает, что вам все равно придется обрабатывать несколько кодовых точек. Во-вторых, некоторые платформы, такие как Windows, используют UTF-16 в качестве кодировки wchar_t
, что означает, что одиночный wchar_t
даже не обязательно является значением кода. (Является ли использование UTF-16 таким способом даже в соответствии со стандартом, неоднозначно. Стандарт требует, чтобы каждый символ, поддерживаемый локалью, был представлен в виде единого значения wchar_t
; если ни одна локаль не поддерживает какой-либо символ вне BMP, тогда UTF-16 может рассматриваться как соответствующий.)