What encoding are filenames in NTFS stored as?
NTFS stores filenames in UTF-16, however fopen
is using ANSI (not UTF-8).
In order to use an UTF16-encoded file name you will need to use the Unicode versions of the file open calls. Do this by defining UNICODE
and _UNICODE
in your project. Then use the CreateFile
call or the wfopen
call.
fopen() - in MSVC on windows does not (by default) take a utf-8 encoded char*.
Unfortunately utf-8 was invented rather recently in the great scheme of things. Windows APIs are divided into Unicode and Ansi versions. every windows api that takes or deals with strings is actually available with a W or A suffix - W for "Wide" character/Unicode and A for Ansi. Macro magic hides all this away from the developer so you just call CreateFile with either a char* or a wchar_t* depending on your build configuration without knowing the difference.
The 'Ansi' encoding is actually not a specific encoding:- But means that the encoding used for "char" strings is specific to the locale setting of the PC.
Now, because c-runtime functions - like fopen - need to work by default without developer knowledge - on windows systems they expect to receive their strings in the windows local encoding. msdn indicates the microsoft c-runtime api setlocal can change the locale of the current thread - but specifically says that it will fail for any locales that need more than 2 bytes per character - like utf-8.
So, on Windows there is no shortcut. You need to use wfopen, or the native API CreateFileW (or create your project using the Unicode build settings and just call Createfile) with wchar_t* strings.
As answered by others, the best way to handle UTF-8-encoded strings is to convert them to UTF-16 and use native Unicode APIs such as _wfopen
or CreateFileW
.
However, this approach won't help when calling into libraries that use fopen()
unconditionally because they do not support Unicode or because they are written in portable C. In that case it is still possible to make use of the legacy "short paths" to convert a UTF-8-encoded string into an ASCII form usable with fopen
, but it requires some legwork:
Convert the UTF-8 representation to UTF-16 using
MultiByteToWideChar
.Use
GetShortPathNameW
to obtain a "short path" which is ASCII-only.GetShortPathNameW
will return it as a wide string with all-ASCII content, which you will need to trivially convert it to a narrow string by a lossless copy casting eachwchar_t
char
.Pass the short path to
fopen()
or to the code that will eventually usefopen()
. Be aware that error messages printed by that code, if any, will refer to the unsightly "short path" (e.g.KINTO~1
instead ofkinto-un-筋斗雲
).
While this is not exactly a recommended long-term strategy, as Windows short paths are a legacy feature that can be turned off per-volume, it is likely the only way to pass file names to code that uses fopen()
and other file-related API calls (stat
, access
, ANSI versions of CreateFile
and similar).