2017-02-22 48 views
1

Я уже много часов читал о Unicode, его кодировках и многих связанных с ними темах.
Причина моего исследования заключается в том, что я пытаюсь прочитать содержимое файла и проанализировать их по характеру.C++ Правильно читайте файлы, чьи символы в Юникоде могут быть больше байта

Поправьте меня, если я ошибаюсь, пожалуйста:

C++ 's getc() возвращает int, который может равняться EOF.
Если возвращаемое значение не равно EOF, оно может быть интерпретировано как , надежно присвоенное char.
С std::string основан на char мы можем построить std::string с этими символами и использовать их.

У меня есть фон C#, где мы используем C# char (16 бит) для string s.
Значение этих char s отображается непосредственно в значениях Unicode.
A char, значение которого равно 5, равно знаку Юникода, расположенному по адресу U+0005.

Я не понимаю, как читать файл на C++, содержащий символы, значения которых могут быть больше байта. Я не чувствую себя комфортно, используя getc(), когда я могу читать только символы, значения которых ограничены байтом.

Возможно, мне не хватает важного момента, как правильно читать файлы с помощью C++.
Любые идеи очень ценятся.

Я запускаю Windows 10 x64 с помощью vC++.
Но я бы предпочел оставить этот вопрос независимым от платформы, если это возможно.

EDIT

Я хотел бы подчеркнуть, переполнение стека пост, связанный в комментариях Klitos Kyriacou (?):
How well is Unicode supported in C++11?

Это быстрое погружение, как плохо юникод поддерживается в C++.
Для получения дополнительной информации вы должны прочитать/посмотреть ресурсы, предоставленные в принятом ответе.

+0

У вас была возможность взглянуть на 'std :: wstring' и/или' wchar_t'? –

+2

Какую кодировку вы хотите использовать? –

+0

@ VadaPoché Позвольте мне прочитать этот материал ... –

ответ

0

Я рекомендую посмотреть Unicode in C++ by James McNellis.
Это поможет объяснить, что облегчает C++, имеет и не имеет при работе с Unicode.
Вы увидите, что C++ не имеет хорошей поддержки для простой работы с UTF8.

Поскольку вы хотите перебирать каждый символ (не только кодовые точки),
Я бы рекомендовал использовать библиотеку 3-го уровня для обработки тонкостей.
utfcpp работал хорошо для меня.

+0

Разговор, который вы связываете, дает некоторые важные сведения о поддержке Unicode в C++. Я могу рекомендовать его всем, кто хочет лучше понять кодировки символов в gerneral (не только C++). Я поеду с utfcpp, потому что из того, что я понял, он, как представляется, обеспечивает лучшую функциональность для 1) проверки и 2) преобразования. –

2

Эквивалент для 16-разрядного символа, совместимого с Windows API, будет wchar_t.Имейте в виду, что wchar_t может быть 32-битным на некоторых платформах, поэтому используйте char16_t, если вы хотите хранить кодированную строку UTF-16 независимым от платформы способом.

Если вы используете char16_t на платформе Windows, вам нужно выполнить некоторые нажатия, хотя при передаче строк в API OS.

эквивалентные типы строк являются:

  • std::wstring (wchar_t)
  • std::u16string (char16_t)

типы файлов потока:

  • std::wifstream (ЬурейеЕ для std::basic_ifstream<wchar_t>)
  • std::basic_ifstream<char16_t>
  • std::wofstream (ЬурейеЕ для std::basic_ofstream<wchar_t>)
  • std::basic_ofstream<char16_t>

Пример прочитать UTF-8 кодированный файл в UTF-16 строка:

#include <windows.h> 
#include <fstream> 
#include <string> 
#include <locale> 
#include <codecvt> 

int main() 
{ 
    std::wifstream file(L"test_utf8.txt"); 

    // Apply a locale to read UTF-8 file, skip the BOM if present and convert to UTF-16. 
    file.imbue(std::locale(file.getloc(), 
     new std::codecvt_utf8_utf16<wchar_t, 0x10ffff, std::consume_header>)); 

    std::wstring str; 
    std::getline(file, str); 

    ::MessageBox(0, str.data(), L"test", 0); 

    return 0; 
} 

Как читать кодированный файл UTF-16 в 16-разрядный std::wstring или std::u16string ?

Видимо, это не так просто. Существует std::codecvt_utf16, но при использовании с 16-битным wchar_t символом он производит UCS-2, который является только подмножеством UTF-16, поэтому суррогатные пары будут считаться неверными. См. cppreference example.

Я не знаю, как комитет по ИСО С ++ пришел к этому решению, потому что на практике он абсолютно бесполезен. По крайней мере, они должны были предоставить флаг, чтобы мы могли выбрать, хотим ли мы ограничить себя в UCS-2 или хотим прочитать полный диапазон UTF-16.

Возможно, есть другое решение, но прямо сейчас я не знаю об этом.

+0

+1 по нескольким причинам: указав wstring и wchar_t, которые я ТЕПЕРЬ считаю, не должны использоваться для UTF8, описывая разные потоки файлов и предоставляя образец для UTF8 и UTF16. Я не принял это как ответ, потому что я считаю, что библиотека, такая как utfcpp (упомянутая Trevor), может обрабатывать проверку и декодирование намного лучше, чем любой образец, который я могу понять/написать. Тем не менее, если кто-то ищет самостоятельное письменное решение, это, вероятно, путь. Спасибо за информацию zett42 :) –

+0

К сожалению, мне пришлось удалить пример UTF-16, потому что он только читал UCS-2 (см. Выше). Следует внимательно прочитать документацию ... – zett42

+0

Просто прочитайте файл как двоичный файл. Используйте машины стандартной библиотеки, где она работает без усилий, сделайте что-нибудь еще, где это yuck-ish. Иногда в прошлом «делать что-то еще» включало в себя запись кодеков UTF-8 с нуля, но теперь с C++ 11 и более поздними версиями библиотека не достаточно yuck-ish в этом отношении, чтобы оправдать усилия. –

1

Ситуация в том, что C getc() был написан в 1970-х годах. Во всех смыслах и целях это означает «читать октет», а не «читать символ». Практически все двоичные данные построены на октетах.

Unicode позволяет вводить символы за пределами диапазона, который может представлять октет. Итак, наивно, люди Юникода предложили стандарт для 16-битных символов. Затем Microsoft включила предложение на ранней стадии и добавила широкие символы (wchar_t и т. Д.) В Windows. Одна из проблем заключалась в том, что 16 бит недостаточно, чтобы представлять каждый глиф на каждом человеческом языке с некоторым статусом, а другой - с контентом двоичных файлов. Таким образом, пользователям Unicode пришлось добавить 32-разрядный стандарт unicode, а затем они включили в начале Unicode-файлов небольшой тег enianness и format. Наконец, 16-битные символы Unicode не совсем соответствовали глифам wchar_t от Microsoft.

Таким образом, результат был беспорядок. Трудно читать и отображать 16 или 32-битные Unicode-файлы с полной точностью и переносимостью. Кроме того, очень многие программы по-прежнему использовали 8 бит ascii.

К счастью, UTF-8 был изобретен.UTF-8 обратно совместим с 7-разрядным ascii. Если верхний бит установлен, тогда глиф кодируется более чем одним символом, и есть схема, которая сообщает вам, сколько. Нулевой байт никогда не появляется, кроме как индикатор конца строки. Поэтому большинство программ будут обрабатывать UTF-8 правильно, если только они не попытаются разделить строки или иначе попытаться рассматривать их как английский.

UTF-8 имеет штраф, что случайный доступ к символам невозможен из-за правила переменной длины. Но это незначительный недостаток. Как правило, UTF-8 - это способ сохранить текст Юникода и передать его в программах, и вы должны только разбить его на кодовые точки Юникода, когда вам действительно нужны глифы, например. для показа.

+0

+1 для обеспечения истории. Следует также сказать, что UTF-16 очень подвержен ошибкам, потому что, даже если разработчики не знают суррогатных пар, он будет работать 99% времени, потому что эти разработчики больше всего будут испытывать только с кодовыми точками в диапазоне UCS-2 , – zett42

+0

* UTF-8 имеет штраф, что случайный доступ к символам невозможен * ... это также верно для UTF-16 и даже UTF-32, поскольку [абстрактный символ] (https://en.wikipedia.org/wiki/Unicode # Abstract_characters) может состоять из нескольких символов Unicode. – zett42