wchar_t file streams
The std::wfstream is similar to std::fstream except it accepts wchar_t. However it does not write std::wchar_t characters to file. Suppose the following code:
std::wofstream ofs{L"c:\\temp\\1.txt" , std::ios_base::out | std::ios_base::binary};
ofs.write(L"ABC", 3);
On the Windows platform this writes just single bytes characters to the file. It uses the codecvt of the imbued locale which translated wchar_t to char. The standard C locale does not handle characters above the 255 so it will fail when using other characters than the extended ASCII character set. It will also fail when writing binary data through the write interface. It can be fixed by using a custom locale which leaves wchar_t unaffected. There was a codeproject article on this but it has been retracted.
This translation is quite unexpected behavior since the function prototypes are defined in terms of wchar_t. It is also different compared to the wchar_t string streams: std::wstringstream does write wchar_t strings unaffected.
This article was inspired by a YouTube comment of me where I stated that the C file stream API is less surprising. Of course there is always a clown who thinks better but probably doesn't know anything about above issue. With C stream I/O FILE and 'fwrite' the bytes are transferred to the file without interpretation and alteration.
FILE wrapper
Jason Turner goes a lengthy way of wrapping the C file stream API but using a wrapper class would probably be simpler:
class c_file
{
public:
c_file()
: m_fp(nullptr)
{
}
explicit c_file(const std::filesystem::path& rpth, const char* pszMode)
: m_fp(fopen(rpth.string().c_str(), pszMode))
{
}
~c_file()
{
if (m_fp)
{
fclose(m_fp);
}
}
c_file(const c_file&) = delete;
c_file(c_file&& rOther) noexcept
: m_fp(std::exchange(rOther.m_fp, nullptr))
{
}
c_file& operator=(const c_file&) = delete;
c_file& operator=(c_file&& rOther) noexcept
{
std::swap(m_fp, rOther.m_fp);
}
explicit operator bool() const
{
return m_fp;
}
size_t read(void* pBuffer, size_t size, size_t count)
{
return fread(pBuffer, size, count, m_fp);
}
size_t write(const void* pBuffer, size_t size, size_t count)
{
return fwrite(pBuffer, size, count, m_fp);
}
// etc.
private:
FILE* m_fp;
};
Links