Q: переносимые имена файлов в локальных кодировках (C++)
lexa - 29/Дек/2011 12:59
А вот, извиняюсь, вопрос.
Вот есть имя файла в национальной кодировке и я его хочу fopen(). На Винде и на Маке одним куском кода (хе-хе).
Насколько я сумел это изучить, ситуация такая:
- Win32: или я отдаю в fopen() 8-битную кодировку (1251), или в _wfopen() в wchar_t (UCS-16?)
- Mac: отдаем в fopen() UTF-8 и нам щастье
- Linux: не знаю, пока руки не дошли.
Вопрос: есть какой-то совместимый способ, одинаковый на всех помянутых системах, или так и придется #ifdef WIN32...?
Comments
В Linux можно уже считать что "отдаем в utf-8 и будет счасть
В Linux можно уже считать что "отдаем в utf-8 и будет счастье".
Уже несколько версий как все дистрибутивы по умолчанию ставят локаль utf-8.
А GNOME начал навязывать utf-8 как кодировку файловой системы еще несколько раньше.
Так что сейчас про фокусы с переменной среды G_FILENAME_ENCODING уже можно забыть.
Тем более что за пределами Gtk-based GUI-шных программ никто на эту переменную особо и не смотрел.
В Win32 если очень извратиться можно выставить utf-8 (aka code page 65001) в качестве "национальной" кодировки.
65001 - не выход, пользователя не заставишь это сделать. То
65001 - не выход, пользователя не заставишь это сделать.
То есть получается две ветки кода, одна с wchar_t, вторая с UTF8, так что ли?
Не надо заставлять это делать ПОЛЬЗОВАТЕЛЯ. Сильно извратит
Не надо заставлять это делать ПОЛЬЗОВАТЕЛЯ. Сильно извратиться - значит сделать это внутри программы.
Хотя, похоже проще сделать свою обертку вокруг fopen, которая будет сначала звать MultiByteToWideChar с явным указанием 65001, а потом уже _wfopen.
И #ifdef _WIN32 останется во одном include.
Вопрос мой, на самом деле, распадается на два 1) Что делать
Вопрос мой, на самом деле, распадается на два
1) Что делать с библиотекой? Я подозреваю, что для Win32 нужно кроме open_file(const char*fname) дать еще open_wfile(const wchar_t *), а на остальных системах - не давать.
2) Что делать с каким-то собственным приложением для end-user. Так как там имена файлов исходно юникодные (потому что Qt's QString), проблем с преобразованием вроде бы нет
А что, в Qt еще своей обертки для открытия файлов, получающе
А что, в Qt еще своей обертки для открытия файлов, получающей QString нет? Я думал что там уже это, совсем для всего обертки есть. Если уж они свой database abstraction layer написали...
А в библиотеке для win32 нужно еще бы TCHAR поддержать. Который в зависимости от не помню уже каких дефайнов будет либо char, либо wchar_t.
В Qt все есть, но что мне с их файлов проку?
В Qt все есть, но что мне с их файлов проку?
Если есть все, то должен быть и fileno
Если есть все, то должен быть и fileno
Да, он есть, но в том месте, куда передается filename (LibRa
Да, он есть, но в том месте, куда передается filename (LibRaw) - нельзя передать дескриптор.
И делать такой интерфейс, с дескриптором, не хочется, по куче причин. Передадут туда сокет (в котором нету seek) и что с ним делать?
Что с ним делать понятно. На то в errno.h есть EBADF. А как
Что с ним делать понятно. На то в errno.h есть EBADF. А как его возвращать вызывающему - ну так же как возвррается EIO.
Но в общем я именно за то и не люблю Qt и им подобные, что они крайне плохо стыкуются со всем что должно поддерживтаь еще что-то кроме них.
Не, к сожалению функциональности нужно больше, чем есть у со
Не, к сожалению функциональности нужно больше, чем есть у сокета. Например, нужно уметь открыть JPEG2000 stream (jasper-ом). Ну и там еще немного.
То есть это можно делать над буфером (и делается) или над char *filename, а с просто int filehandle - не получается.
Посему - от Qt мне нужно только имя файла. И там для этого все есть, оно в std::wstring умеет сконвертировать. Проблема лишь в том, что в виндах нужно одно (wstring), а в остальных системах - другое (utf8 в char*)
Да вероятнее всего utf8 и все
Да вероятнее всего utf8 и все будет хорошо.
Но я когда совсем недавно столкнулся с этим вопросом делал конверсию так:
На всех платформах принимаю имя utf8 encoded. Конвертирую в wchar_t (на windows это получается utf16, на Linux нормальные 32bit ISO10646), все строковые операции в wchar_t
Далее на win _wfopen(), на linux делаю конверсию в установленную пользователем locale
То есть вероятнее всего в обратно в utf8. Но если у кого-то koi8-r, то нормально.
Но надо помнить, что не всегда по полученому имени файла можно будет восстановить оригинальное имя в Unicode.
Использовать code page 65001 оказалось проблематично, подробности я забыл но их помнит google.
Попадалась мне некоторое время назад статья, как разные файл
Попадалась мне некоторое время назад статья, как разные файловые системы воспринимают различным образом написанные уникодные имена (в смысле, символ или комбинация символа с диакритиками).
Вот, кажется http://nedbatchelder.com/blog/201106/filenames_
Вот, кажется
http://nedbatchelder.com/blog/201106/filenames_with_accents.html
Да-да, про разницу в виде UTF в Mac/Linux я уже прочитал в и
Да-да, про разницу в виде UTF в Mac/Linux я уже прочитал в исходниках Qt (QFile), это тоже мне доставило.
Под Windoze в общем случае только _wfopen() потому что 8-бит
Под Windoze в общем случае только _wfopen() потому что 8-битовая кодировка зависит от локали.
Впрочем, перекодировать из UTF8 довольно просто, так что один из возможных подходов - решить, что библиотека работает только в UTF8, и уже внутри перекодировать как надо.
Можно, впрочем, и в обратную сторону примерно так же.
Вроде в unix-ах (в mac видимо аналогично) стандартные ф-ии н
Вроде в unix-ах (в mac видимо аналогично) стандартные ф-ии не работают с wchar, поэтому там мультибайтный UTF-8. Казалось бы в Windows надо просто выбрать utf8 как специальную кодовую страницу, но похоже так нельзя, так можно только при конвертации. В итоге надо использовать wchar.
http://stackoverflow.com/questions/166503/utf-8-in-windows
Вообще-то под Виндой
Вообще-то под Виндой "безобидный" char*, прилетающий от системы (через GUIшные контролы или через параметры командной строки) - это вовсе не восьмибитовые ACSII-строки, как можно было бы ожидать, а строки в так называемой multi-byte кодировке.
Для русского языка этот MBCS вырождается в ASCII, так как одного байта вполне хватает для таблицы cp1251, а вот во всяких японских и китайских локалях один символ на экране соответствует двум и более char'ам в строке, которую получит приложение.
Ну и сама система работает с этим зоопарком в согласованном стиле, то есть если мы получили на вход (то есть от пользователя) строку в MBCS, то можем вполне передавать эту строку далее, во всякие там CreateFile и прочие функции. Соответственно, можно ожидать, что Cшный fopen нормально отреагирует на аргумент в MBCS (с MS VC это точно так, насчет других компиляторов нужно проверять).
Всё это, естественно, только в том случае, если по-китайски вводят имя файла в системе с установленным системным китайским языком.
Для русской системы китайская MBCS-строку - полная чушь и наоборот.
То есть если вдруг понадобится работать сразу с несколькими языками, то придется всё делать только через wchar, тут уж без вариантов.
Подождите, у винды есть два
Подождите, у винды есть два fopen:
fopen(const char *path,...)
_wfopen(const wchar_t *path,...)
И мне казалось, что multibyte char передается во вторую, иначе зачем бы она вдруг была нужна.
У Винды есть CreateFileA и
У Винды есть CreateFileA и CreateFileW.
Первая работает с обнобайтовыми строками в кодировке MBCS (то есть со строками, каждый символ которой занимает 1 или 2 байта; по идее может быть и большее количество, но в MSDN указывается, что символы длинее 2х байтов не поддерживаются), вторая с честными двух-байтовыми юникодными строками (каждый символ которых всегда занимает ровно 2 байта).
C-шные fopen и _wfopen внутри используют именно CreateFileA и CreateFileW, соответственно.
Более подробная информация по этим ссылкам:
http://msdn.microsoft.com/en-us/library/5z097dxa.aspx
http://msdn.microsoft.com/en-us/library/cwe8bzh0.aspx
Очень хорошо про разницу MBCS и юникода расписано тут:
http://www.codeproject.com/KB/string/cppstringguide1.aspx
В некотором смысле, виндовый MBCS - это аналог UTF8, так как тоже имеются специальные управляющие коды и разные символы кодируются различным количеством байтов.
Грамотно сделанные сайты
Грамотно сделанные сайты "решают это" легко: спрашиваем - отвечают.
Что мешает при установке/перезапуске спросить систему: "На якой мови гутарим?"
1) Я не понял, причем тут
1) Я не понял, причем тут сайты
2) Вопрос вообще не про это. А вот про что:
Вот у меня есть имя файла, полученное через Qt File Selection Dialog, лежит в QString в виде Unicode (в каком-то там представлении). Есть ли способ это преобразовать так, чтобы fopen() удовлетворился бы этим преобразованием на *любой* системе (Win, Mac, Linux)
Знание языка/кодовой страницы для этого, по идее, вовсе не нужно.
ИМХО, способ простой: ДО
ИМХО, способ простой: ДО инициализации функционала либы спросить систему о кодировке. Либо средствами самой либы, либо положить её в "оболочку" - при обращении к либе "оболочка" либы спрашивает систему о кодировке и передаёт "параметры" в либу.
Ага, я - воинствующий профан. Согласен заранее!
Да, это не дело либы -
Да, это не дело либы - разбираться в осях и кодировках, но если нужен универсальность и работоспособность функционала..., смиритесь и положите "это" внутрь.
Это как проблема выбора статической либо динамической линковки.
PS. я сам всё про себя знаю. :-)
Да кодировку я, допустим,
Да кодировку я, допустим, знаю.
Вопрос то в другом:
- в mac/linux можно давать в fopen просто utf-8
- в винде - нельзя
Поэтому суть вопроса в том, есть ли какой-то способ, который одинаково работает во всех трех системах. Похоже, нету, значит код надо писать с #ifdef