Взаимодействие кодовой страницы Windows со стандартными именами файлов C/C++?

Клиент жалуется, что наш код использовался для записи файлов с японскими символами в имени файла, но теперь работает не во всех случаях. Мы всегда просто использовали старые добрые строки char * для представления имен файлов, поэтому меня немного шокировало, что это когда-либо работало, и мы не сделали ничего, о чем я знаю, что должно было бы заставить его перестать работать. Я попросил их прислать мне файл со встроенным именем файла, экспортированным из нашего программного обеспечения, и похоже, что в строках используются шестнадцатеричные символы 82 и 83 в качестве первого символа двухбайтовой последовательности для представления японских символов. Поиск в Интернете приводит меня к мысли, что это, вероятно, SHIFT_JIS и/или кодовая страница Windows 932.

Мне кажется, что то, что происходит, ранее было как fopen, так и ofstream::open принятыми именами файлов с использованием этой кодовой страницы; теперь только fopen делает. Я проверил документы Visual Studio fopen и не вижу намека на то, что делает приемлемую строку для передачи в fopen.

В краткосрочной перспективе я надеюсь, что кто-то сможет пролить свет на конкретную проблему Windows fopen и ofstream::open для меня. В конечном счете, мне бы очень хотелось узнать общепринятый способ открытия имен файлов Unicode (и других?) в C++, в Windows, Linux и OS X.

Отредактировано для добавления: я считаю, что открытия, которые работают, выполняются в локали «C», тогда как те, которые не работают, выполняются в любой локали клиента по умолчанию. Однако так было уже много лет, и старая версия программы до сих пор работает в их системе, так что это кажется далеким объяснением проблемы, которую мы наблюдаем.

Обновление: я отправил заказчику небольшую тестовую программу. Было подтверждено, что fopen нормально работает с именем файла SHIFT_JIS, а std::ofstream — нет. Это происходит в Visual Studio 2005 и происходит независимо от того, использовал ли я локаль по умолчанию или локаль «C».

Мне все еще интересно, есть ли у кого-нибудь объяснение этому поведению (и почему оно таинственным образом изменилось - возможно, пакет обновлений VS2005?) и надеюсь собрать исчерпывающий "лучший опыт" для обработки имен файлов Unicode в переносимом коде C++.


person Sol    schedule 26.01.2009    source источник
comment
Возможно, вы могли бы назвать временные рамки, когда это произошло. Windows сильно изменилась за эти годы.   -  person Joe Soul-bringer    schedule 26.01.2009
comment
Хорошая точка зрения. Изменение произошло в течение последнего года.   -  person Sol    schedule 26.01.2009


Ответы (6)


Такие функции, как fopen или ofstream::open, принимают имя файла как char *, но это интерпретируется как системная кодовая страница.

Это означает, что это может быть японский символ, представленный как Shift-JIS (cp932), или упрощенный китайский (Big 5/cp936), корейский, арабский, русский, как вы его называете (при условии, что он соответствует системной кодовой странице ОС).

Это также означает, что он может использовать японские имена файлов только в японской системе. Измените системную кодовую страницу, и приложение «перестанет работать». Я подозреваю, что это то, что происходит здесь (в Windows нет больших изменений со времен Win 2000 в этой области).

Вот как вы меняете кодовую страницу системы: http://www.mihai-nita.net/article.php?artID=20050611a

В долгосрочной перспективе вы можете подумать о переходе на Unicode (и использовать _wfopen, wofstream).

person Mihai Nita    schedule 09.02.2009
comment
Поскольку я обновил вопрос, странно то, что fopen работает с кодовой страницей, а ofstream::open - нет. Кроме того, действительно ли _wfopen и wofstream переносимы? - person Sol; 17.02.2009
comment
Такие функции, как fopen или ofstream::open, принимают имя файла как char *, но это интерпретируется как системная кодовая страница. Извините, я не верю в это. fopen и ofstream::open — это функции в библиотеках C и C++, поэтому по умолчанию они должны использовать локаль C. Если приложение хочет, чтобы функции CRT использовали языковой стандарт Windows, оно должно вызвать функцию языкового стандарта CRT. - person Windows programmer; 27.04.2010
comment
Извините, я не верю. ... они должны по умолчанию использовать локаль C. =› Ну попробуй! - person Mihai Nita; 24.11.2011

Я не знаю ни одного переносимого способа использования файлов Unicode с использованием системных библиотек по умолчанию. Но есть некоторые фреймворки, которые предоставляют переносимые функции, например:

  • для C: glib использует имена файлов в UTF-8. ;
  • для C++: glibmm также использует имена файлов в UTF-8, требуется glib;
  • для C++: boost может использовать wstring для имена файлов.

Я почти уверен, что фреймворки .NET/mono также содержат переносимые функции файловой системы, но я их не знаю.

person Tometzky    schedule 03.02.2009

Я почти уверен, что в Linux строка имени файла представляет собой строку UTF-8 (например, в файловой системе EXT3 запрещены только символы косой черты и NULL), хранящиеся в обычном формате char *. На странице руководства, похоже, не упоминается кодировка символов, что наводит меня на мысль, что это системный стандарт UTF-8. OS X, скорее всего, использует то же самое, поскольку у него схожие корни, но я менее уверен в этом.

person rmeador    schedule 26.01.2009
comment
Нет, все нативные файловые системы Linux игнорируют кодировку символов (однако некоторые не нативные FS заботятся об этом). Имена файлов представляют собой строки байтов, и единственными специальными символами являются косая черта и ноль. Любые кодировки должны обрабатываться оболочкой. - person Zan Lynx; 26.01.2009

Возможно, вам придется установить локаль потока на локаль системы по умолчанию. См. здесь возможные причины ваших проблем: http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=100887

person Stefan    schedule 26.01.2009
comment
Хм... это интересно. Глядя на мой код, возможно, что открытые, которые работают, всегда находятся в локали C, тогда как те, которые терпят неудачу, находятся в любой машине пользователя. Однако это не то, что недавно изменилось с нашей стороны.... - person Sol; 26.01.2009
comment
Вы обновили визуальную студию? Если да, то это изменение на вашей стороне. Если нет, то извините, у меня закончились идеи... - person Stefan; 26.01.2009
comment
Нет, везде Visual Studio 2005. - person Sol; 27.01.2009

Mac OS X использует Unicode в качестве собственной кодировки символов. Базовыми строковыми объектами являются CFString и NSString. Они хранят массив символов как Unicode.

person mouviciel    schedule 05.02.2009

Это еще кто-то смотрит? Я только что исследовал этот вопрос и нигде не нашел ответов, поэтому я могу попытаться объяснить свои выводы здесь.

В VS2005 обработка имени файла fstream является странной: она не использует системную кодировку по умолчанию, ту, которую вы получаете с помощью GetACP и устанавливаете в Панели управления/Регионе и Языке/Административном. Но всегда CP 1252 -- я думаю.

Это может вызвать большую путаницу, и Microsoft удалила эту особенность в более поздних версиях VS.

Все обходные пути для VS2005 имеют свои недостатки:

  1. Преобразуйте свой код, чтобы использовать Unicode везде

  2. Никогда не открывайте fstreams, используя имена файлов с узкими символами, всегда конвертируйте их в Unicode, используя системную кодировку по умолчанию, используйте имя файла с широкими символами open/ctor

  3. Получите кодовую страницу с помощью GetACP(), затем выполните

соответствующий setlocale:

setlocale (LC_ALL, ("." + lexical_cast<string> (GetACP())).c_str())
person Pjacobi    schedule 09.08.2013