You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++中如何将UTF-8文件读取到std::string中?

用标准C++17实现跨平台UTF-8文件读取到std::string

答案是肯定的——完全可以用标准C++17满足你的所有需求,下面分两种方案介绍,分别对应「简单直接读取」和「流库集成(注入locale facet)」的场景:

方案一:二进制模式直接读取(最简洁,满足所有核心要求)

既然所有文件都是UTF-8编码,我们只需要把文件的字节原封不动读入std::string即可,不需要任何编码转换。这种方法完全独立于系统区域设置,跨平台兼容性拉满,还能避开Windows下文本模式的换行符自动转换问题。

代码示例:

#include <fstream>
#include <string>
#include <stdexcept>

std::string read_utf8_file(const std::string& file_path) {
    // 以二进制+末尾定位模式打开文件,方便获取文件大小
    std::ifstream file(file_path, std::ios::binary | std::ios::ate);
    if (!file.is_open()) {
        throw std::runtime_error("无法打开目标文件");
    }

    // 获取文件大小并分配内存
    const std::streamsize file_size = file.tellg();
    file.seekg(0, std::ios::beg);
    std::string content(static_cast<std::size_t>(file_size), '\0');

    // 读取全部内容
    if (!file.read(content.data(), file_size)) {
        throw std::runtime_error("读取文件内容失败");
    }

    return content;
}

为什么这个方案符合要求?

  • 严格保持UTF-8内容:直接读取字节,无任何编码转换
  • 跨平台兼容:std::ifstream的二进制模式在所有目标系统(macOS 10.13+、Windows 10+、Android、iOS 10+)的C++17标准库中都支持
  • 避开std::wstring:全程只用std::string
  • 独立于区域设置:不依赖系统默认locale,完全靠二进制读取逻辑

方案二:自定义Locale Facet实现流的无转换读取(满足流库支持需求)

如果你希望用标准流库(比如std::ifstream的文本模式)处理,同时让流直接读取UTF-8到std::string而不做转换,可以自定义一个codecvt facet,告诉流不需要进行任何编码转换,直接将文件字节映射到char(也就是std::string的存储单元)。

第一步:实现自定义codecvt facet

#include <locale>
#include <cstring>

// 自定义codecvt facet,完全不做编码转换
class utf8_no_convert_facet : public std::codecvt<char, char, std::mbstate_t> {
protected:
    // 输入转换:直接复制字节
    result do_in(std::mbstate_t&, 
                 const char* from, const char* from_end, const char*& from_next,
                 char* to, char* to_end, char*& to_next) const override {
        const std::size_t copy_len = std::min(static_cast<std::size_t>(from_end - from), 
                                              static_cast<std::size_t>(to_end - to));
        std::memcpy(to, from, copy_len);
        from_next = from + copy_len;
        to_next = to + copy_len;
        return copy_len == static_cast<std::size_t>(from_end - from) ? result::ok : result::partial;
    }

    // 输出转换:直接复制字节(如果需要写入UTF-8文件也能用)
    result do_out(std::mbstate_t&,
                  const char* from, const char* from_end, const char*& from_next,
                  char* to, char* to_end, char*& to_next) const override {
        const std::size_t copy_len = std::min(static_cast<std::size_t>(from_end - from), 
                                              static_cast<std::size_t>(to_end - to));
        std::memcpy(to, from, copy_len);
        from_next = from + copy_len;
        to_next = to + copy_len;
        return copy_len == static_cast<std::size_t>(from_end - from) ? result::ok : result::partial;
    }

    // 无需补全字符
    result do_unshift(std::mbstate_t&, char* to, char* to_end, char*& to_next) const override {
        to_next = to;
        return result::ok;
    }

    // 单字节编码(符合UTF-8的字节流特性)
    int do_encoding() const noexcept override { return 1; }

    // 始终不进行转换
    bool do_always_noconv() const noexcept override { return true; }

    // 计算可转换的字节数
    int do_length(std::mbstate_t&, const char* from, const char* from_end, std::size_t max) const override {
        return static_cast<int>(std::min(static_cast<std::size_t>(from_end - from), max));
    }
};

第二步:将facet注入Locale并应用到流

你可以选择给单个流设置,或者设置全局Locale(影响所有流):

#include <fstream>
#include <string>
#include <iterator>

// 方式1:给单个流设置自定义locale
std::string read_utf8_with_stream(const std::string& file_path) {
    std::ifstream file(file_path);
    // 给流注入自定义facet,保留原有locale的其他特性
    file.imbue(std::locale(file.getloc(), new utf8_no_convert_facet));

    // 用流迭代器读取全部内容
    return std::string(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
}

// 方式2:设置全局locale(所有流默认使用)
void setup_global_utf8_locale() {
    std::locale::global(std::locale(std::locale(), new utf8_no_convert_facet));
}

注意事项

  • Windows文本模式的换行符转换:如果用文本模式打开文件,Windows会自动将CRLF转换成LF。如果需要严格保持文件的原始字节,记得打开流时加上std::ios::binary。
  • 全局Locale的影响:设置全局Locale会影响所有后续的流操作,如果只是部分流需要处理UTF-8,建议给单个流imbue自定义locale。
  • UTF-8合法性检查:上述方案不会验证读取的内容是否为合法UTF-8,如果需要,可以在读取后添加额外的验证逻辑(比如检查字节序列是否符合UTF-8编码规则)。

方案对比

方案优点适用场景
二进制直接读取代码简洁,性能高,完全避免locale干扰不需要流库特性,仅需读取文件内容
自定义facet流读取兼容标准流库生态,支持流的各种操作(比如逐行读取)需要用流处理UTF-8文件,或集成现有流相关代码

内容的提问来源于stack exchange,提问作者Clearer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:22:03