Почему делает широкий поток файла в C++ узкие записанные данные по умолчанию?

Я попробовал несколько опций, и я обосновываюсь на MVVM как лучший выбор для меня. Смешиваемость является важным моментом, и я также нахожу аспект VM интуитивным для оснащения динамических поведений и процедурных эффектов и анимаций (как Silverlight Nikhil. FX). Однажды я старался избегать Смешения в целом через быстрые интерфейсы, но нахожу связь между UI и поведением слишком болезненной в конечном счете. Я хочу разработать свой UI в Смешении и затем добавить эффекты и другие поведения в коде, это оказывается лучшим шаблоном для меня для следования до сих пор.

18
задан Community 23 May 2017 в 12:07
поделиться

5 ответов

Модель, используемая C ++ для кодировок, унаследована от C и восходит как минимум к 1989 году.

Два основных момента:

  • Операции ввода-вывода выполняются в терминах char.
  • задача локали - определить, насколько широкие символы сериализованы
  • локаль по умолчанию (названная "C") очень минимальна (я не помню ограничений из стандарта, здесь он может обрабатывать только 7-битный ASCII как узкий и широкий набор символов).
  • существует определяемая средой локаль с именем ""

Итак, чтобы получить что-либо, вы должны установить локаль.

Если я использую простую программу

#include <locale>
#include <fstream>
#include <ostream>
#include <iostream>

int main()
{
    wchar_t c = 0x00FF;
    std::locale::global(std::locale(""));
    std::wofstream os("test.dat");
    os << c << std::endl;
    if (!os) {
        std::cout << "Output failed\n";
    }
}

], которые используют языковой стандарт среды и выводят в файл широкий символ кода 0x00FF. Если я прошу использовать языковой стандарт «C», я получаю

$ env LC_ALL=C ./a.out
Output failed

, что языковой стандарт не может обрабатывать широкий символ, и мы получаем уведомление о проблеме, поскольку сбой ввода-вывода.

7
ответ дан 30 November 2019 в 08:21
поделиться

Очень частичный ответ на первый вопрос: файл представляет собой последовательность байтов, поэтому при работе с wchar_t по крайней мере должно произойти некоторое преобразование между wchar_t и char . Выполнение этого преобразования «разумно» требует знания кодировок символов, поэтому этому преобразованию разрешено быть зависимым от локали на основании использования фасета в локали потока.

Тогда возникает вопрос, как это преобразование должно быть быть изготовленным в единственной локали, требуемой стандартом: «классической». На этот вопрос нет "правильного" ответа, и поэтому в стандарте это очень расплывчато. Из вашего вопроса я понимаю, что вы предполагаете, что слепое приведение (или memcpy () - ing) между wchar_t [] и char [] было бы хорошим способом. Это небезосновательно, и на самом деле это то, что (или, по крайней мере, было) сделано в некоторых реализациях.

Другая точка зрения будет заключаться в том, что, поскольку codecvt является аспектом локали, разумно ожидать, что преобразование выполняется с использованием «кодировка локали» (я здесь не совсем точен, так как концепция довольно расплывчата). Например, можно было бы ожидать, что турецкая локаль будет использовать ISO-8859-9, а японская - использовать Shift JIS. По сходству «классический» языковой стандарт будет преобразован в эту «кодировку языкового стандарта». По-видимому, Microsoft предпочла просто обрезать (что приводит к IS-8859-1, если мы предположим, что wchar_t представляет UTF-16 и что мы остаемся в базовой многоязычной плоскости), в то время как реализация Linux, о которой я знаю, решила придерживаться ASCII.

По вашему второму вопросу:

Кроме того, получим ли мы настоящие потоки Unicode с C ++ 0x или я что-то здесь упускаю?

В [ locale.codecvt] в n2857 (последний черновик C ++ 0x, который у меня есть), можно прочитать:

Специализация codecvt преобразуется между UTF-16 и схемы кодирования UTF-8, а специализация codecvt преобразует схемы кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 :

По вашему второму вопросу:

Кроме того, получим ли мы настоящие потоки Unicode с C ++ 0x или мне здесь чего-то не хватает?

В разделе [locale.codecvt] n2857 (последняя версия C + + 0x draft, что у меня под рукой) можно прочитать:

Специализация codecvt преобразует схемы кодирования UTF-16 и UTF-8, а также специализацию codecvt выполняет преобразование между схемами кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 :

По вашему второму вопросу:

Кроме того, получим ли мы настоящие потоки Unicode с C ++ 0x или мне здесь чего-то не хватает?

В разделе [locale.codecvt] n2857 (последняя версия C + + 0x draft, что у меня под рукой) можно прочитать:

Специализация codecvt преобразует схемы кодирования UTF-16 и UTF-8, а также специализацию codecvt выполняет преобразование между схемами кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 : получим ли мы настоящие потоки Unicode с C ++ 0x или я что-то здесь упускаю?

В разделе [locale.codecvt] n2857 (последний черновик C ++ 0x, который у меня есть) можно прочитать:

Специализация codecvt преобразует схемы кодирования UTF-16 в UTF-8, а специализация codecvt преобразует между схемами кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 : получим ли мы настоящие потоки Unicode с C ++ 0x или я что-то здесь упускаю?

В разделе [locale.codecvt] n2857 (последний черновик C ++ 0x, который у меня есть под рукой) можно прочитать:

Специализация codecvt преобразует схемы кодирования UTF-16 в UTF-8, а специализация codecvt преобразует между схемами кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 : выполняет преобразование между схемами кодирования UTF-16 и UTF-8, а специализация codecvt преобразует между схемами кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 : выполняет преобразование между схемами кодирования UTF-16 и UTF-8, а специализация codecvt преобразует между схемами кодирования UTF-32 и UTF-8. codecvt преобразует собственные наборы символов для узких и широких символов.

В разделе [locale.stdcvt] мы находим:

Для аспекта codecvt_utf8 : - Фасет должен преобразовывать многобайтовые последовательности UTF-8 в UCS2 или UCS4 (в зависимости от размера Elem) в программе. [...]

Для фасета codecvt_utf16 : - Фасет должен преобразовывать многобайтовые последовательности UTF-16 в UCS2 или UCS4 (в зависимости от размера Elem) в программе. [...]

Для фасета codecvt_utf8_utf16 : - Фасет должен преобразовывать многобайтовые последовательности UTF-8 в UTF-16 (один или два 16-битных кода) в программе.

Так что, я думаю, это означает «да», но вам нужно быть более точным о том, что вы подразумеваете под "настоящими потоками Unicode", чтобы быть уверенным.

13
ответ дан 30 November 2019 в 08:21
поделиться

Я не знаю о wofstream. Но C ++ 0x будет включать новые типы символов distict (char16_t, char32_t) с гарантированной шириной и подписью (без знака), которые можно переносимо использовать для UTF-8, UTF-16 и UTF-32. Вдобавок появятся новые строковые литералы (u «Hello!» Для строкового литерала в кодировке UTF-16, например)

Ознакомьтесь с самым последним проектом C ++ 0x (N2960) .

3
ответ дан 30 November 2019 в 08:21
поделиться

Что касается вашего первого вопроса, то это мое предположение.

Библиотека IOStreams была построена в нескольких помещениях относительно кодирования. Например, для преобразования между Unicode и другими, не очень обычными кодировками предполагается, что.

  • Внутри вашей программы вы должны использовать (фиксированной ширины) кодировку широких символов.
  • Только внешнее хранилище должно использовать (переменную- width) многобайтовых кодировок.

Я считаю, что это причина существования двух шаблонных специализаций std :: codecvt. Один из них сопоставляется между типами char (возможно, вы просто работаете с ASCII), а другой - между wchar_t (внутренним для вашей программы) и char (внешними устройствами). Поэтому всякий раз, когда вам нужно выполнить преобразование в многобайтовую кодировку, вы должны делать это побайтово. Обратите внимание, что вы можете написать фасет, который обрабатывает состояние кодирования, когда вы читаете / записываете каждый байт из / в многобайтовую кодировку.

При таком подходе поведение стандарта C ++ становится понятным. В конце концов, вы используете строки в кодировке ASCII с широкими символами (при условии, что это значение по умолчанию на вашей платформе, и вы не переключили языковые стандарты). «Естественным» преобразованием будет преобразование каждого символа ASCII с расширенными символами в обычный (в данном случае один символ) символ ASCII. (Преобразование существует, и оно простое.)

Кстати, я не уверен, знаете ли вы, но вы можете избежать этого, создав фасет, который возвращает noconv для преобразований. Тогда у вас будет файл с расширенными символами.

При таком мышлении поведение стандарта C ++ понятно. В конце концов, вы используете строки в кодировке ASCII с широкими символами (при условии, что это значение по умолчанию на вашей платформе, и вы не переключили языковые стандарты). «Естественным» преобразованием было бы преобразование каждого символа ASCII с расширенными символами в обычный (в данном случае один символ) символ ASCII. (Преобразование существует, и оно простое.)

Кстати, я не уверен, знаете ли вы, но вы можете избежать этого, создав фасет, который возвращает noconv для преобразований. Тогда у вас будет файл с расширенными символами.

При таком мышлении поведение стандарта C ++ понятно. В конце концов, вы используете строки в кодировке ASCII с широкими символами (при условии, что это значение по умолчанию на вашей платформе, и вы не переключили языковые стандарты). «Естественным» преобразованием будет преобразование каждого символа ASCII с расширенными символами в обычный (в данном случае один символ) символ ASCII. (Преобразование существует, и оно простое.)

Кстати, я не уверен, знаете ли вы, но вы можете избежать этого, создав фасет, который возвращает noconv для преобразований. Тогда у вас будет файл с расширенными символами.

преобразование будет заключаться в преобразовании каждого символа ASCII расширенного символа в обычный (в данном случае один символ) символ ASCII. (Преобразование существует, и оно простое.)

Кстати, я не уверен, знаете ли вы, но вы можете избежать этого, создав фасет, который возвращает noconv для преобразований. Тогда у вас будет файл с расширенными символами.

преобразование будет заключаться в преобразовании каждого символа ASCII расширенного символа в обычный (в данном случае один символ) символ ASCII. (Преобразование существует, и оно простое.)

Кстати, я не уверен, знаете ли вы, но вы можете избежать этого, создав фасет, который возвращает noconv для преобразований. Тогда у вас будет файл с расширенными символами.

2
ответ дан 30 November 2019 в 08:21
поделиться

Проверьте это: Class basic_filebuf

Вы можете изменить поведение по умолчанию, установив широкий буфер char, используя pubsetbuf. Как только вы это сделаете, вывод будет wchar_t, а не char.

Другими словами, для вашего примера вы получите:

wofstream file(L"Test.txt", ios_base::binary); //binary is important to set!  
wchar_t buffer[128];  
file.rdbuf()->pubsetbuf(buffer, 128);  
file.put(0xFEFF); //this is the BOM flag, UTF16 needs this, but mirosoft's UNICODE doesn't, so you can skip this line, if any.  
file << someString; // the output file will consist of unicode characters! without the call to pubsetbuf, the out file will be ANSI (current regional settings)  
2
ответ дан 30 November 2019 в 08:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: