как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локалью?
От: Кодт Россия  
Дата: 20.06.14 12:56
Оценка:
Задача вот какая.
Есть кроссплатформенный проект (windows, linux), пользующийся некоторыми сторонними библиотеками.
В том числе, эти библиотеки принимают char* имена файлов, чтобы внутри себя их прочесть-записать.
Нужно как-то изловчиться, чтобы они могли работать с именами за пределами системной локали.

Например, у меня русские винды, кодировка 1251 и 866.
Беру файл с умляутами (1252), — в эксплорере он виден, в коммандкоме тоже.
fopen() обламывается.

Конечно, в виндах есть std::ifstream с конструктором (wchar_t*), а кроссплатформенно есть boost::ifstream (под виндами работает с utf-16, под никсами — с utf-8).
Но это если я на своей стороне делаю. А мне надо, чтобы сторонняя библиотека тоже смогла.

Было бы идеально взять имя в utf-8 и передать. Но библиотека же не подозревает об этом, — значит, придётся через setlocale.
И тут засада: в виндах setlocale не знает про utf-8, ни так ".utf-8", ни так "utf-8", ни даже так ".65001".

Есть какой-нибудь способ навязать utf-8 сишному рантайму? Пусть даже windows-specific?


Ладно, допустим, я ограничусь работой только с файлами, чьи имена нормально представляются в системной кодировке.
Но вот существуют модификации кодировок: 1251 русская, казахская и чувашская, 1254 турецкая и азербайджанская.
Беру, скажем, конфиг (в utf-8 или utf-16), перевожу в дефолтную кодировку штатными средствами, — имя правильно отобразится, или нет? Как он отличит, это 1254-t или 1254-a?
А потом полученную строку fopen() примет?


К сожалению, яндекс и гугл завален "детскими вопросами" — как выводить всякие кодировки на экран, или как побороть пэхапэ. Поэтому буду благодарен за точную наводку.
Перекуём баги на фичи!
Re: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локалью?
От: _Dreamer Россия  
Дата: 20.06.14 13:30
Оценка: 30 (1)
Здравствуйте, Кодт, Вы писали:

К>Задача вот какая.

К>Есть кроссплатформенный проект (windows, linux), пользующийся некоторыми сторонними библиотеками.
К>В том числе, эти библиотеки принимают char* имена файлов, чтобы внутри себя их прочесть-записать.
К>Нужно как-то изловчиться, чтобы они могли работать с именами за пределами системной локали.

К>К сожалению, яндекс и гугл завален "детскими вопросами" — как выводить всякие кодировки на экран, или как побороть пэхапэ. Поэтому буду благодарен за точную наводку.


Вот тут уже предлагают то, что я хотел и сам предложить (а значит это не так и глупо), а именно:
Спросить имя файла у самой системы.
http://stackoverflow.com/questions/21402742/fopen-with-unicode-filename

Но у подхода есть ограничения, которые там же и обсуждаются.
Есть еще вариант переписать библиотеку, но не всем это подойдет =)
windows fopen unicode
Re: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локалью?
От: alex_public  
Дата: 20.06.14 17:29
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Задача вот какая.

К>Есть кроссплатформенный проект (windows, linux), пользующийся некоторыми сторонними библиотеками.
К>В том числе, эти библиотеки принимают char* имена файлов, чтобы внутри себя их прочесть-записать.
К>Нужно как-то изловчиться, чтобы они могли работать с именами за пределами системной локали.

К>Например, у меня русские винды, кодировка 1251 и 866.

К>Беру файл с умляутами (1252), — в эксплорере он виден, в коммандкоме тоже.
К>fopen() обламывается.

Ну так в винде то всё API работает через юникод (utf-16), так что никаких проблем и не должно быть.

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

Естественно, если у нас в юникодном имени создаваемого файла находятся только символы входящие в системную кодировку, то можно без проблем преобразовать его в системную и передать в fopen — всё будет корректно. А вот для произвольного имени боюсь так не выйдет.

К>Было бы идеально взять имя в utf-8 и передать. Но библиотека же не подозревает об этом, — значит, придётся через setlocale.

К>И тут засада: в виндах setlocale не знает про utf-8, ни так ".utf-8", ни так "utf-8", ни даже так ".65001".

К>Есть какой-нибудь способ навязать utf-8 сишному рантайму? Пусть даже windows-specific?


Сомневаюсь.

К>Ладно, допустим, я ограничусь работой только с файлами, чьи имена нормально представляются в системной кодировке.

К>Но вот существуют модификации кодировок: 1251 русская, казахская и чувашская, 1254 турецкая и азербайджанская.
К>Беру, скажем, конфиг (в utf-8 или utf-16), перевожу в дефолтную кодировку штатными средствами, — имя правильно отобразится, или нет? Как он отличит, это 1254-t или 1254-a?
К>А потом полученную строку fopen() примет?

А какая разница то? Есть просто системная кодировка и всё. Пока в строках только символы из неё, всё будет нормально и с не юникодным API.
Re[2]: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локаль
От: lxa http://aliakseis.livejournal.com
Дата: 20.06.14 20:12
Оценка: 9 (1)
Я подумал, если GetShortPathName() не пройдет, можно использовать CreateHardLink() как последнее убежище.
Re: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локалью?
От: c-smile Канада http://terrainformatica.com
Дата: 20.06.14 20:14
Оценка:
Здравствуйте, Кодт, Вы писали:

К>А потом полученную строку fopen() примет?


Можно попробовать GetShortPathNameW
Она по идее должна сделать что-то ASCII compatible. И уже это имя отдавать в ту либу.

Ну и как вариант переименовывать файлы перд отдачей в ascii имена перед отдачей, а потом обратно. Грустно, но может быть the only option.
Re: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локалью?
От: Pavel Dvorkin Россия  
Дата: 21.06.14 08:36
Оценка:
Здравствуйте, Кодт, Вы писали:

К>Было бы идеально взять имя в utf-8 и передать. Но библиотека же не подозревает об этом, — значит, придётся через setlocale.

К>И тут засада: в виндах setlocale не знает про utf-8, ни так ".utf-8", ни так "utf-8", ни даже так ".65001".

UTF8 и locale — перепендикулярны. Либо локаль (объяснение, как понимать 8-битные символы), либо UTF8, в которой все символы есть.

К>Есть какой-нибудь способ навязать utf-8 сишному рантайму? Пусть даже windows-specific?


Скорее всего нет, по крайней мере надежного. Дело в том, что fopen в Windows ведет на CreateFileA, куда практически без изменений передается файловый путь. А CreateFileA, похоже, использует системную локаль для интерпретации этих 8-битных символов, преобразуя их при этом в порядочные Unicode символы, которые и хранятся хоть в FAT, хоть в NTFS.

Можешь проверить. Поставь в Control Panel — Region And Language — Administrative — Language for Non-Unicode Programs немецкую локаль в качестве системной и вызови fopen с умляутами. Думаю, се пройдет, зато перестанут, скорее всего, создаваться файлы с русскими именами, либо же они окажутся немецкими .

Я пробовал перед fopen ставить немецкую локаль с помощью SetThreadLocale. Увы, не помогает. Но и точного указания, как именно CreateFileA интерпретирует в этом плане строку, я тоже не нашел. Так что все это ИМХО.
With best regards
Pavel Dvorkin
Re[2]: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локаль
От: Кодт Россия  
Дата: 21.06.14 20:41
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

К>>И тут засада: в виндах setlocale не знает про utf-8, ни так ".utf-8", ни так "utf-8", ни даже так ".65001".

PD>UTF8 и locale — перепендикулярны. Либо локаль (объяснение, как понимать 8-битные символы), либо UTF8, в которой все символы есть.

Ошибаешься. Локаль — это совокупность граней (facet), описывающих разные правила — формат чисел, сравнение строк, и т.д. И в том числе — кодировку.
Только в никсах кодировка utf-8 поддерживается сишным рантаймом, а в винде — нет.
#include <clocale>
#include <cstdio>

int main()
{
  puts(setlocale(LC_ALL, ""));
}

ru_RU.UTF-8, говорит мне убунту; russian_RUSSIA.1251, говорит винда

Возможно, что в винде слишком крепко и глубоко внедрено ограничение в 2 байта на символ и в 2 байта на номер кодировки.
И UTF-8 и UTF-7 поддерживаются лишь функциями MultiByteToWideChar / WideCharToMultiByte.

Да вообще, эти числовые идентификаторы LCID и CP в винде — признак кривого дизайна ещё с досовского наследства.

К>>Есть какой-нибудь способ навязать utf-8 сишному рантайму? Пусть даже windows-specific?


PD>Скорее всего нет, по крайней мере надежного. Дело в том, что fopen в Windows ведет на CreateFileA, куда практически без изменений передается файловый путь. А CreateFileA, похоже, использует системную локаль для интерпретации этих 8-битных символов, преобразуя их при этом в порядочные Unicode символы, которые и хранятся хоть в FAT, хоть в NTFS.


Надо будет как-нибудь подебажить CRT...
Я когда-то давно лазил туда, — душераздирающее зрелище. Особенно меня убило, как по-разному сделаны wprintf и wostream.


Пока что короткие досовские имена являются выходом. В их сторону и буду смотреть. Либо просто скажу заказчику "ССЗБ".
Перекуём баги на фичи!
Re[3]: как подчинить библиотеку, чтоб работала с юникодом или, хотя бы, с локаль
От: Pavel Dvorkin Россия  
Дата: 22.06.14 17:00
Оценка:
Здравствуйте, Кодт, Вы писали:


К>>>И тут засада: в виндах setlocale не знает про utf-8, ни так ".utf-8", ни так "utf-8", ни даже так ".65001".

PD>>UTF8 и locale — перепендикулярны. Либо локаль (объяснение, как понимать 8-битные символы), либо UTF8, в которой все символы есть.

К>Ошибаешься. Локаль — это совокупность граней (facet), описывающих разные правила — формат чисел, сравнение строк, и т.д. И в том числе — кодировку.


Я имел в виду именно кодировку, естественно, а не формат даты и валюты. Хотя мог бы быть точнее, верно.


К>Только в никсах кодировка utf-8 поддерживается сишным рантаймом, а в винде — нет.


О чем, собственно, я и говорил. CreateFileA принимает строку и интерпретирует ее в соответствии с системной кодировкой (локалью)

К>Возможно, что в винде слишком крепко и глубоко внедрено ограничение в 2 байта на символ и в 2 байта на номер кодировки.

К>И UTF-8 и UTF-7 поддерживаются лишь функциями MultiByteToWideChar / WideCharToMultiByte.

Видимо, да.


К>Надо будет как-нибудь подебажить CRT...


Там дебажить нечего (в плане fopen). Через несколько посредников выход на CreateFile. Путь при этом передается вроде как без изменений.

К>Пока что короткие досовские имена являются выходом. В их сторону и буду смотреть. Либо просто скажу заказчику "ССЗБ".


Успехов!
With best regards
Pavel Dvorkin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.