Здравствуйте, pepsicoca, Вы писали:
P>Тогда скажите, чем отличается std::wofstream от std::ofstream? В Вашей формулировке они не отличаются ничем. Тогда какой смысл иметь std::wofstream, если он ничем не отличатется от std::ofstream?
Смысл в том, чтобы открыть файл с содержанием "Hello World!!!" (в кодировке ASCII), и прочитать эту строчку в std::wstring с помощью
std::wofstream, созданного без флага std::ios::binary, чтобы можно было потом отдать её (std::wstring строку) туда, где хотят
wchar_t* текст. Т.е. если в софтине вы используете широкие строки, то пользуется std::wstring/
std::wofstream/
std::wifstream/
std::wstringstream, если в софтине вы используете однобайтовые строки, то используете
std::string/
std::ofstream/
std::ifstream/
std::stringstream соответственно. При этом и
std::ofstream и
std::wofstream будет подразумевать, что в файле лежит однобайтовый текст, закодированный в активной системной локали, просто внутри программы они будут оперировать с разными символами
char и
wchar_t соответственно.
Как вы наверняка знаете,
std::basic_ofstream/
std::basic_ifstream могут работать в двух режимах: текстовом и бинарном. Это определяется отсутствием или наличием флага std::ios::binary соответственно. Текстовый режим работает по умолчанию. Бинарный включается руками во время конструирования или во время вызова метода open(). Если
std::basic_ofstream работает в текстовом режиме, то он вправе изменять переданные строки перед сохранием, например он заменяет символ '\n' на последовательность символов "\r\n" на винде, на символ '\n' на Линуксе и на символ '\r' на маке. То же саме для
std::basic_ifstream, только в обратную сторону. Если
std::basic_ofstream работает в бинарном режиме, то он просто пишет то что ему дали и всё.
Текстовыми файлами являются файлы которые содержат данные в однобайтовой кодировке, например ASCII. Остальные файлы являются бинарными: то есть для их корректного чтения и правильного представления в памяти необходимы соответствующие алгоритмы. Если в файле хранится юникод-текст в кодировке UTF-8, то в самом начале файла лежит сигнатура, состоящая из байтов EF BB BF, если в кодировке UTF-16BE, то сигнатура — FE FF, если UTF-16LE — FF FE, если UTF-32BE или UTF-32LE, то 00 00 FE FF и FF FE 00 00. Так вот эти сигнатуры "говорят" нам: "мы не просто текстовые файлы, мы файлы с некими данными, хранящимися в определённом формате, и чтобы работать с этими данными, вам понадобятся определённые алгоритмы".
Чтобы прочитать простой текстовый файл программе, использующей
char* строки, необходимо:
открыть этот файл с помощью
std::ifstream
возможно, потребуется сделать вызов strm.unsetf(
std::ios::skipws), чтобы не поскипать пробелы, табуляции, переходы строк и прочие пустые символы
читать оттуда строки и помещать их в
char* контейнер, например в
std::string
Чтобы записать простой текстовый файл программе, использующей
char* строки, необходимо:
открыть этот файл с помощью std::оfstream
писать туда строки из
char* контейнеров, например из
std::string
Чтобы прочитать простой текстовый файл программе, использующей
wchar_t* строки, необходимо:
открыть этот файл с помощью
std::wifstream
возможно, потребуется сделать вызов strm.unsetf(
std::ios::skipws), чтобы не поскипать пробелы, табуляции, переходы строк и прочие пустые символы
читать оттуда строки и помещать их в
wchar_t* контейнер, например в std::wstring
Чтобы записать простой текстовый файл программе, использующей
wchar_t* строки, необходимо:
открыть этот файл с помощью std::wоfstream
писать туда строки из
wchar_t* контейнеров, например из std::wstring
Чтобы прочитать бинарный файл программе, не важно какие строки использующей, необходимо:
открыть этот файл с помощью
std::ifstream, указав флаг std::ios::binary
hint: при этом не надо использовать
std::wifstream для этих целей, потому что каждый байт файла будет помещён в
wchar_t переменную, что логически не верно (я не знаю точно таким ли будет поведение
std::wifstream, однако предпочитаю и советую использовать для бинарного чтения узкие потоковые классы)
не помню, надо ли здесь strm.unsetf(
std::ios::skipws) делать :)
читать оттуда блоки данных и помещать их в
char* контейнер
работать с этими данными
Чтобы записать бинарный файл программе, не важно какие строки использующей, необходимо:
открыть этот файл с помощью std::оfstream, указав флаг std::ios::binary
hint: при этом не надо использовать std::wоfstream для этих целей, потому что каждое двубайтие будет впихнуто только в один байт при записи в файл, при этом будет происходить потеря старшего байта двубайтия (я не знаю точно таким ли будет поведение std::wоfstream, однако предпочитаю и советую использовать для бинарной записи узкие потоковые классы)
записать туда бинарные данные, взятые из
char* контейнера
Чтобы прочитать юникодный текстовый файл программе, использующей
char* строки, необходимо:
открыть этот файл с помощью
std::ifstream
узнать кодировку этого файла, используя хранящуююся в начале соответствующую сигнатуру (или если её там нет, то тогда софтина должна "знать" в какой unicode-кодировке ей подсунули текст, например она может спросить это у юзверя)
возможно, потребуется сделать вызов strm.unsetf(
std::ios::skipws), чтобы не поскипать пробелы, табуляции, переходы строк и прочие пустые символы
читать оттуда строки и помещать их в
char* контейнер, например в
std::vector<[i]char*>[/i]
транслировать полученные данные из известной кодировки (UTF-8/UTF-16BE/UTF-16LE/etc.) в текстовую кодировку, с которой работает программа, например CP1251, при этом символы, эквивалента которых нет в целевой кодировке (например CP1251) будут заменены какой-то хернёй; результат поместить в в
char* контейнер, например в
std::string
работать с полученным однобайтовым текстом
Чтобы записать юникодный текстовый файл программе, использующей
char* строки, необходимо:
открыть этот файл с помощью std::оfstream
транслировать свои данные из кодировки, с которой работает программа, в нужную кодировку (UTF-8/UTF-16BE/UTF-16LE/etc.) и поместить результат в
char* контейнер, например
std::vector<[i]char*>[/i]
записать в начало файла сигнатуру нужной out кодировки (например для UTF-8 -> EF BB BF)
писать в файл данные из полученного
char* контейнера, в котором хранится юникодный текст
Чтобы прочитать юникодный текстовый файл программе, использующей
wchar_t* строки, необходимо:
открыть этот файл с помощью
std::ifstream
узнать кодировку этого файла, используя хранящуююся в начале соответствующую сигнатуру (или если её там нет, то тогда софтина должна "знать" в какой unicode-кодировке ей подсунули текст, например она может спросить это у юзверя)
возможно, потребуется сделать вызов strm.unsetf(
std::ios::skipws), чтобы не поскипать пробелы, табуляции, переходы строк и прочие пустые символы
читать оттуда строки и помещать их в
char* контейнер, например в
std::vector<[i]char*>[/i]
транслировать полученные данные из известной кодировки (UTF-8/UTF-16BE/UTF-16LE/etc.) в текстовую кодировку, с которой работает программа, например USC-2 на винде позднее Windows NT или UTF-16LE на винде позднее Windows 2000, результат поместить в в
wchar_t* контейнер, например в std::wstring
работать с полученным двубайтовым текстом
Чтобы записать юникодный текстовый файл программе, использующей
wchar_t* строки, необходимо:
открыть этот файл с помощью std::оfstream
транслировать свои
wchar_t* строки из кодировки, с которой работает программа (например USC-2 на винде позднее Windows NT или UTF-16LE на винде позднее Windows 2000), в нужную кодировку (UTF-8/UTF-16BE/UTF-16LE/etc.) и поместить результат в
char* контейнер, например
std::vector<[i]char*>[/i]
записать в начало файла сигнатуру нужной out кодировки (например для UTF-16LE -> FF FE)
писать в файл данные из полученного
char* контейнера, в котором хранится юникодный текст
Для того чтобы и на входе (чтение unicode-текста из файлов), и на выходе (запись unicode-текста в файлы), и внутри алгоритмов программы поддержать не все возможные кодировки Юникода (UTF-8/UTF-16BE/UTF-16LE/etc.), а только лишь одну широкую кодировку (например нативную для винды — UTF-16LE), и при этом пользовать потоковые широкие классы, люди конфигурируют свои широкие потоковые классы с помощью фацетов и локалей, примерно как здесь:
standard-file-streams-and-stdlocale_20.html или здесь:
read-unicode-text-file-in-c.html. При этом, конечно, теряется вся гибкость работы с Unicode-кодировками, т.е. программа, использующая std::wstring и отконфигурированные std::wofstream и std::wifstream, и работающая на Windows 2000 and later, может писать и читать только в кодировке UTF-16LE, и если ей потребуется записать/почитать текст в другой unicode-кодировке (UTF-8/UTF-16BE/USC-2/UTF-32BE/UTF-32LE/etc.), то ей придётся заморочиться с пунктами, приведенными выше.
Возврящаясь к нашим баранам. Т.е. к консоли. Если консоль — есть главное устройство ввода/вывода и интеракции с пользователем, как в GNU/Linux например, то ей по статусу положено быть навороченной, писать текст всеми цветами радуги, в локальной кодировке и в кодировках Юникода и пр. Если же консоль — аппендикс операционки, пережиток времён DOS'а, и главным устройством ввода/вывода и интеракции с пользователем является ГУЙ, как в винде например, тогда такой консоли по статусу не положено быть навороченной. Т.е. вы можете представить себе консоль, как простой текстовый файл, данные в котором хранятся в активной в системе локальной однобайтовой кодировке. У винды с русской локализацией (CP1251) консоль работает в кодировке DOS-866? чтобы поддержать всякой досовское старьё видимо. Соответственно для того чтобы
std::wcout, который как мы знаем на винде с активной русской локалью CP1251 пишет текст в кодировке CP1251, для того чтобы
std::wcout мог внятно по-русски написать в консоль, надо ему подсказать, что данные, которые ему заходят через оператор << в текстовой кодировке CP1251, в целевом файле (т.е. в консоли) должны сидеть в кодировке DOS-866, а не в кодировке CP1251, и это достигается вызовом
std::wcout.imbue(std::locale("rus_rus.866"));. Вот в принципе и всё.
"Дайте мне возможность выпускать и контролировать деньги в государстве и – мне нет дела до того, кто пишет его законы." (c) Мейер Ансельм Ротшильд , банкир.