В какой кодировке хранятся имена файлов в NTFS?

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

В частности, в какой кодировке (UTF-8, UTF-16LE / BE) находятся имена файлов (не содержание, а фактическое имя файла), хранящиеся в NTFS? Можно ли открыть любой файл с помощью fopen (), который принимает char *, или у меня нет другого выбора, кроме как использовать wfopen (), который использует wchar_t * и предположительно принимает строку UTF-16?

Я попытался вручную ввести строку в кодировке UTF-8 в fopen (), например.

unsigned char filename[] = {0xEA, 0xB0, 0x80, 0x2E, 0x74, 0x78, 0x74, 0x0}; // 가.txt

FILE* f = fopen((char*)filename, "wb+");

но получилось "ê ° € .txt".

У меня сложилось впечатление (что может быть ошибочным), что строки в кодировке UTF8 будет достаточно для открытия любого имени файла под Windows, потому что я, кажется, смутно помню, как какое-то приложение Windows передавало (char *), а не (wchar_t *), и имело Нет проблем.

Может кто-нибудь пролить некоторый свет на это?


person vroooom    schedule 12.01.2010    source источник
comment
Внешний вид PHP изменился с PHP 7.1, см. stackoverflow.com/a/38466772/680382   -  person Gogowitsch    schedule 17.06.2017
comment
У меня сложилось впечатление (что может быть ошибочным), что строки в кодировке UTF8 будет достаточно для открытия любого имени файла в Windows - Windows НЕ поддерживает имена файлов в кодировке UTF-8, только UTF-16 и ANSI ( который внутренне конвертируется в UTF-16). Однако имена файлов UTF-8, содержащие только символы ASCII, будут работать как строки ANSI. Я смутно помню, как какое-то приложение Windows передавало (char), а не (wchar_t), и у него не было проблем - char* не подразумевает UTF-8, но можно использовать для этого. Никакие стандартные API-интерфейсы файлов Win32 или C / C ++ не принимают UTF-8 в качестве входных данных, но сторонние библиотеки могут   -  person Remy Lebeau    schedule 17.01.2019


Ответы (3)


NTFS хранит имена файлов в UTF-16, однако fopen использует ANSI (не UTF-8).

Чтобы использовать имя файла в кодировке UTF16, вам необходимо использовать версии Unicode для вызовов открытия файла. Сделайте это, определив UNICODE и _UNICODE в своем проекте. Затем используйте вызов CreateFile или вызов wfopen.

person villintehaspam    schedule 12.01.2010
comment
Если изменение проекта для сборки с определенным UNICODE слишком велико, вы можете вызвать wfopen() или CreateFileW() в сборке, отличной от Unicode. - person Michael Burr; 12.01.2010
comment
Учитывая, что Windows NT и NTFS старше стандарта UTF-16, возможно ли, что вместо него будет использоваться более старый UCS-2? - person hillu; 30.10.2012
comment
NTFS допускает любую последовательность 16-битных значений для кодировки имени, кроме 0x0000. Это означает, что поддерживаются кодовые точки UTF-16, но файловая система не проверяет, является ли последовательность допустимой для UTF-16. [источник] - person user; 06.07.2014
comment
Функции Unicode @hillu Win32 используют wchar_t строки. NT и NTFS могут предшествовать UTF-16, но wchar_t может использоваться как для UCS-2, так и для UTF-16 в Windows, а Microsoft перешла с UCS-2 на использование UTF-16 в Windows 2000 и далее. - person Remy Lebeau; 17.01.2019

fopen () - в MSVC для Windows (по умолчанию) не принимает char * в кодировке utf-8.

К сожалению, по большому счету UTF-8 был изобретен сравнительно недавно. Windows API делятся на версии Unicode и Ansi. каждый API Windows, который принимает или работает со строками, на самом деле доступен с суффиксом W или A - W для "широкого" символа / Unicode и A для Ansi. Макро-магия скрывает все это от разработчика, поэтому вы просто вызываете CreateFile с помощью char * или wchar_t * в зависимости от конфигурации сборки, не зная разницы.

Кодировка «Ansi» на самом деле не является конкретной кодировкой: - но означает, что кодировка, используемая для строк «char», зависит от настроек локали ПК.

Теперь, поскольку функции c-runtime, такие как fopen, должны работать по умолчанию без ведома разработчика, в системах Windows они ожидают получить свои строки в локальной кодировке Windows. msdn указывает, что microsoft c-runtime api setlocal может изменить языковой стандарт текущего потока, но, в частности, говорит, что он не сработает для любых языков, которым требуется более 2 байтов на символ - например, utf-8.

Итак, в Windows нет ярлыка. Вам необходимо использовать wfopen или собственный API CreateFileW (или создать свой проект с использованием настроек сборки Unicode и просто вызвать Createfile) со строками wchar_t *.

person Chris Becke    schedule 12.01.2010
comment
На самом деле есть ярлык: вы можете преобразовать строку UTF-8 в Unicode, создать короткий путь только для ASCII, используя GetShortPathNameW и передайте это fopen. Это единственный способ передать имена файлов, отличные от ASCII, в унаследованные библиотеки (или написанные на переносимом C), которые просто используют fopen для открытия файлов. - person user4815162342; 07.11.2014
comment
каждый API Windows, который принимает или работает со строками, на самом деле доступен с суффиксом W или A - W для широкого символа / Unicode и A для Ansi - БОЛЬШИНСТВО функций, но не КАЖДОЙ функции. Функции, которые существуют уже долгое время, особенно с тех времен, когда Windows была основана на ANSI, безусловно, существуют. Но новые функции, представленные в последние годы и в будущем, как правило, имеют только широкие версии и не имеют суффикса W. Microsoft хочет постепенно отказаться от ANSI. - person Remy Lebeau; 17.01.2019

Как ответили другие, лучший способ обрабатывать строки в кодировке UTF-8 - это преобразовать их в UTF-16 и использовать собственные API-интерфейсы Unicode, такие как _wfopen или CreateFileW.

Однако этот подход не поможет при вызове библиотек, которые безоговорочно используют fopen(), потому что они не поддерживают Unicode или потому что они написаны на переносимом языке C. В этом случае все еще можно использовать устаревшие «короткие пути» для преобразования строка в кодировке UTF-8 в форму ASCII, которую можно использовать с fopen, но это требует некоторой работы:

  1. Преобразуйте представление UTF-8 в UTF-16, используя _ 5_.

  2. Используйте GetShortPathNameW, чтобы получить "короткий путь", который является только ASCII. GetShortPathNameW вернет его как широкую строку с содержимым, полностью состоящим из ASCII, которое вам нужно будет тривиально преобразовать в узкую строку путем преобразования без потерь копии каждого wchar_t char.

  3. Передайте короткий путь к fopen() или к коду, который в конечном итоге будет использовать fopen(). Имейте в виду, что сообщения об ошибках, напечатанные этим кодом, если таковые имеются, будут относиться к неприглядному «короткому пути» (например, KINTO~1 вместо kinto-un-筋斗雲).

Хотя это не совсем рекомендованная долгосрочная стратегия, поскольку короткие пути Windows являются устаревшей функцией, которую можно отключить для каждого тома, это, вероятно, единственный способ передать имена файлов коду, который использует fopen() и другие API, связанные с файлами. звонки (stat, access, ANSI версии CreateFile и аналогичные).

person user4815162342    schedule 07.11.2014
comment
Шикарная, вы спасли нас, СПАСИБО !! - person Eric; 09.05.2015
comment
для обработки строк в кодировке UTF-8 ... преобразовать их в Unicode UTF-8 (и UTF-16) являются кодировками Unicode. Я думаю, вы имели в виду преобразовать в UTF-16 - person leonbloy; 16.01.2019
comment
@leonbloy Да, я имел в виду Unicode, как это определено в Windows. Пункт №1 дает понять, что необходима кодировка UTF-16. Теперь я изменил ответ, чтобы с самого начала ссылаться на UTF-16. - person user4815162342; 17.01.2019
comment
Решение с коротким путем работает только для чтения файлов, а не для записи, верно? - person skjerns; 26.12.2020
comment
@skjerns Эту стратегию также можно применить в письменной форме. Просто создайте пустой файл с желаемым именем, используя open(name, 'w').close(), а затем продолжайте рецепт. - person user4815162342; 27.12.2020
comment
@ user4815162342 отлично, спасибо! - person skjerns; 28.12.2020