将Unicode字符/字符串写入文件

4

我试图使用std::wofstream将Unicode字符写入文件,但是putwrite函数并没有写入任何字符。

示例代码:

#include <fstream>
#include <iostream>

int main()
{
    std::wofstream file;
    file.open("output.txt", std::ios::app);
    if (file.is_open())
    {
        wchar_t test = L'й';
        const wchar_t* str = L"фывдлао";
        file.put(test);
        file.write(str, sizeof(str));
        file.close();
    }
    else
    {
        std::wcerr << L"Failed to open file" << std::endl;
    }

    std::cin.get();
    return 0;
}

output.txt文件为空,执行代码后没有写入任何wchar/string,这是为什么?我做错了什么吗?

编辑: 更正后的代码:

#include <fstream>
#include <iostream>

int main()
{
    std::wofstream file;
    file.open("output.txt", std::ios::app);
    if (file.is_open())
    {
        wchar_t test = L'й';
        const wchar_t* str = L"фывдлао";
        file.put(test);
        if (!file.good())
        {
            std::wcerr << L"Failed to write" << std::endl;
        }
        file.write(str, 8);
        file.close();
    }
    else
    {
        std::wcerr << L"Failed to open file" << std::endl;
    }

    std::cin.get();
    return 0;
}

应用代码修正后,我看到了“无法写入”的错误,但我仍然不明白如何编写宽字符串和字符。

就我所知,C++中的宽字符存在问题,wchar_t很可能会引起更多问题。像QString这样的替代实现可能是一个更安全的选择。 - Frax
QString?肯定有一种方法可以使用标准库或本地操作系统API处理Unicode吧?我从未遇到过宽字符版本的函数/对象出现意外行为的问题。例如,宽字符版本的offstream处理的是char而不是wchar_t,这背后的逻辑是什么呢? - user11157650
@Frax QString 也是基于 wchar_t 的。它唯一的优势是来自一个稳定版本化的 ABI。 - Deduplicator
@Deduplicator,一方面,QString可以正确处理UTF-8,并且开箱即用。总的来说,它具有可移植性,而wchar_t并非如此,在不同的平台上大小不同。我不确定它在实践中是如何工作的。关键是,cpp标准对wchar_t的规定很少,而QString则相当明确(即稳定的ABI;实际上这很重要)。 - Frax
@Frax 抱歉,我错了。QString 是基于他们自己的 UTF-16 代码单元类,而不是基于 wchar_t - Deduplicator
2个回答

4

我用这种方法解决了问题,不需要使用QString等外部字符串库!

仅使用标准库和c++11

#include <iostream>
#include <locale>
#include <codecvt>
#include <fstream>
#include <Windows.h>

int main()
{
    std::wofstream file;
    // locale object is responsible of deleting codecvt facet!
    std::locale loc(std::locale(), new std::codecvt_utf16<wchar_t> converter);

    file.imbue(loc);
    file.open("output.txt"); // open file as UTF16!

    if (file.is_open())
    {
        wchar_t BOM = static_cast<wchar_t>(0xFEFF);
        wchar_t test_char = L'й';
        const wchar_t* test_str = L"фывдлао";

        file.put(BOM);
        file.put(test_char);
        file.write(test_str, lstrlen(test_str));

        if (!file.good())
        {
            std::wcerr << TEXT("Failed to write") << std::endl;
        }

        file.close();
    }
    else
    {
        std::wcerr << TEXT("Failed to open file") << std::endl;
    }

    std::wcout << TEXT("Done!") << std::endl;

    std::cin.get();
    return 0;
}

File output:

йфывдлао


请注意,std::codecvt_utf16<wchar_t>已被弃用,并且目前没有替代品。您可以考虑将文件保存为UTF8格式,然后转换回UTF16以供WinAPI使用。 - Barmak Shemirani

2
最初的问题发生在这里: put 不支持写入宽字符,流会失败,但是你从未检查第一次写入是否成功。
file.put(test);
if(not file.good())
{
    std::wcerr << L"Failed to write" << std::endl;
}

第二个问题是sizeof(str)返回指针的字节大小,而不是字符串的字节大小。"最初的回答"。

谢谢,我已经更新了我的问题并进行了更正…我需要做什么才能编写宽字符串?你说put不接受宽字符,但我看到它期望一个wchar_t参数,因为它是一个宽版本的wofstream,那么为什么它不起作用? - user11157650
好的,我尝试了std::ios::binary并且只使用write方法,但它仍然无法写出宽字符串。失败位被设置了。 - user11157650
@zebanovich 你确定你已经以二进制模式打开了普通流(而不是宽流,每当无法缩小宽字符时就会失败)吗? - user7860670
@zebanovich 只需尝试 file.write(reinterpret_cast<char const *>(str), wcslen(str) * sizeof(wchar_t)); - user7860670
谢谢,我试过了,输出很糟糕...它写入文件的不是фывдлао,而是DK24;0> - user11157650
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接