Игнорировать метки порядка байтов в C ++, чтение из потока - PullRequest
7 голосов
/ 16 января 2012

У меня есть функция для чтения значения одной переменной (целое, двойное или логическое) в одной строке в ifstream:

template <typename Type>
void readFromFile (ifstream &in, Type &val)
{
  string str;
  getline (in, str);
  stringstream ss(str);
  ss >> val;
}

Однако, это не работает с текстовыми файлами, созданными редакторами, вставляющими спецификацию ( знак порядка байтов ) в начале первой строки, которая, к сожалению, включает панель {Note, Word}. Как я могу изменить эту функцию, чтобы игнорировать метку порядка байтов, если она присутствует в начале str?

Ответы [ 2 ]

13 голосов
/ 16 января 2012

(я предполагаю, что вы работаете в 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 может рассматриваться как соответствующий.)

4 голосов
/ 16 января 2012

Вы должны начать с чтения первого или двух байтов потока и определения, является ли он частью спецификации или нет.Это немного мучительно, поскольку вы можете putback только один байт, тогда как обычно вы хотите прочитать четыре.Самое простое решение - открыть файл, прочитать начальные байты, запомнить, сколько нужно пропустить, затем вернуться к началу и пропустить их.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...