Microsoft have made it impossible to use any standard C or C++ library facilities for opening/closing files on Windows1.

To the naïve observer, fopen or fstream may appear to do the job, but they will only work as long as the path you want to open can be represented in the codepage of the current System Locale. This is because Microsoft, in their wisdom, decided against using UTF-8 stored in a char array as the canonical representation of a path; instead, they chose UTF-16 stored in a wchar_t array. The result is that there is no way for a char array, the standard way to represent paths in C and hence C++, to actually represent all the possible paths on a Windows system. Hence, fstream and other standard facilities can not be reliably used on a Windows system, because they only deal with char paths.

I've tried to convert wchar_t paths to char with mbcstowcs and similar functions, but for paths that cannot be represented in the system locale this merely results in paths that have the offending characters replaced by ? characters. For example, consider the following program:

If run in a directory containing a file with a name such as bi☣hazard 鉄人.txt, it will produce the following output:

Microsoft's C implementation provides alternative facilities that behave like the standard facilities, but that take their paths as wchar_t*, such as _wfopen. Portable programs will have to use these functions and somehow parametrize the type that they use for storing paths.

Microsoft's C++ implementation also extends the standard classes such as std::basic_fstream so that they can deal with wchar_t* paths. This is not available to users of other C++ implementations, however, such as MinGW which uses the GNU Standard C++ Library.

The solution below consists of a basic_win32_ofstream class, which extends the standard basic_ostream class. It uses _wopen to actually open a file; it then gives the resulting file descriptor to a stdio_filebuf, which it attaches to its basic_ostream. The rest of the program can then use the standard std::ostream interface as normal.

I chose to use _wopen & file descriptors rather than _wfopen & FILE*s because the stdio_filebuf automatically closes the former when it is destroyed, but not the latter.

Ugly, but at least the ugliness can be confined to a Windows-specific implementation module. A more complete and powerful alternative would be to use Boost.Filesystem, which provides its own fstream classes that can be initialized from its own platform-independent path classes.


CategoryTechnote

  1. I can't possibly think of the reason why Microsoft would want this to be the case... (1)

robots.org.uk: PortableFilenamesOnWindows (last edited 2017-03-15 12:04:46 by sam)

© Sam Morris <sam@robots.org.uk>.
Content may be distributed and modified providing this notice is preserved.