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

如何实现C++23风格UTF-8转UTF-32的范围遍历(基于Boost.Locale)

实现UTF-8到UTF-32的范围遍历(支持码点位置标记)

一、基于Boost.Locale的实现方案

既然你已经在使用Boost.Locale的normalize功能,直接用它内置的UTF-8迭代器就能满足需求,不需要额外引入其他组件。Boost.Locale的boost::locale::utf::utf8_iterator可以直接解析UTF-8字符串,返回对应的char32_t码点,支持完整的21位Unicode范围(0x0000~0x10FFFF)。

1. 实现管道式范围适配器

先写一个简单的适配器类,让字符串可以通过管道符|调用,适配成支持范围for的UTF-32码点序列:

#include <boost/locale/utf.hpp>
#include <string>

// 定义utf8to32范围适配器
struct utf8to32 {
    const std::string& str;

    explicit utf8to32(const std::string& s) : str(s) {}

    // 使用Boost提供的UTF-8迭代器
    using iterator = boost::locale::utf::utf8_iterator<std::string::const_iterator>;

    iterator begin() const {
        return iterator(str.begin(), str.begin(), str.end());
    }
    iterator end() const {
        return iterator(str.end(), str.begin(), str.end());
    }
};

// 重载管道运算符,支持string | utf8to32{}的写法
utf8to32 operator|(const std::string& str, utf8to32) {
    return utf8to32(str);
}

2. 基础遍历示例

结合你已有的normalize操作,直接遍历码点:

#include <boost/locale.hpp>
#include <iostream>

int main() {
    std::string raw_utf8 = u8"Hello 世界 🌍";
    // 先执行你已有的normalize操作
    std::string normalized = boost::locale::normalize(
        raw_utf8.begin(), raw_utf8.end(), boost::locale::norm_nfc
    );

    // 管道式遍历每个UTF-32码点
    for (char32_t c : normalized | utf8to32{}) {
        std::cout << "码点: U+" << std::hex << static_cast<uint32_t>(c) << std::dec << std::endl;
    }
    return 0;
}

3. 标记码点的起始字节位置

如果需要记录每个码点在原字符串中的起始字节索引,可以通过底层迭代器的位置计算:

size_t byte_pos = 0;
auto str_begin = normalized.begin();
auto str_end = normalized.end();

while (str_begin != str_end) {
    // 构造UTF-8迭代器解析当前码点
    boost::locale::utf::utf8_iterator<std::string::const_iterator> utf_it(str_begin, normalized.begin(), normalized.end());
    char32_t c = *utf_it;

    // 输出码点和起始位置
    std::cout << "码点U+" << std::hex << static_cast<uint32_t>(c) << std::dec
              << " 起始字节位置: " << byte_pos << std::endl;

    // 移动底层迭代器到下一个码点的起始位置
    size_t bytes_used = boost::locale::utf::utf8_length(str_begin, str_end);
    std::advance(str_begin, bytes_used);
    byte_pos += bytes_used;
}

二、自行实现UTF-8解析迭代器

如果无法依赖Boost.Locale,可以手动实现一个符合标准迭代器要求的UTF-8到UTF-32迭代器,支持完整21位码点解析和位置标记:

1. 自定义迭代器实现

#include <string>
#include <cstdint>
#include <stdexcept>
#include <iterator>

class utf8_to_32_iterator {
public:
    using value_type = char32_t;
    using difference_type = std::ptrdiff_t;
    using pointer = const char32_t*;
    using reference = const char32_t&;
    using iterator_category = std::forward_iterator_tag;

    // 默认构造
    utf8_to_32_iterator() : it_(), begin_(), end_(), current_(0), bytes_consumed_(0) {}
    // 带初始位置的构造
    utf8_to_32_iterator(std::string::const_iterator it,
                        std::string::const_iterator begin,
                        std::string::const_iterator end)
        : it_(it), begin_(begin), end_(end), current_(0), bytes_consumed_(0) {
        if (it != end) parse_current();
    }

    // 解引用
    reference operator*() const { return current_; }
    pointer operator->() const { return &current_; }

    // 前置递增
    utf8_to_32_iterator& operator++() {
        advance();
        parse_current();
        return *this;
    }

    // 后置递增
    utf8_to_32_iterator operator++(int) {
        auto temp = *this;
        ++*this;
        return temp;
    }

    // 相等性判断
    bool operator==(const utf8_to_32_iterator& other) const {
        return it_ == other.it_;
    }
    bool operator!=(const utf8_to_32_iterator& other) const {
        return !(*this == other);
    }

    // 获取当前码点的起始字节索引
    size_t start_position() const {
        return std::distance(begin_, it_) - bytes_consumed_;
    }

private:
    std::string::const_iterator it_;
    std::string::const_iterator begin_;
    std::string::const_iterator end_;
    char32_t current_;
    size_t bytes_consumed_;

    // 解析当前位置的UTF-8序列为码点
    void parse_current() {
        if (it_ == end_) {
            current_ = 0;
            bytes_consumed_ = 0;
            return;
        }

        uint8_t first = static_cast<uint8_t>(*it_);
        if ((first & 0x80) == 0) {
            // 1字节ASCII码点
            current_ = first;
            bytes_consumed_ = 1;
        } else if ((first & 0xE0) == 0xC0) {
            // 2字节序列
            check_continuation(1);
            uint8_t second = static_cast<uint8_t>(*std::next(it_));
            current_ = static_cast<char32_t>((first & 0x1F) << 6) | (second & 0x3F);
            bytes_consumed_ = 2;
        } else if ((first & 0xF0) == 0xE0) {
            // 3字节序列
            check_continuation(2);
            uint8_t second = static_cast<uint8_t>(*std::next(it_));
            uint8_t third = static_cast<uint8_t>(*std::next(it_, 2));
            current_ = static_cast<char32_t>((first & 0x0F) << 12) | ((second & 0x3F) << 6) | (third & 0x3F);
            bytes_consumed_ = 3;
        } else if ((first & 0xF8) == 0xF0) {
            // 4字节序列(支持21位码点)
            check_continuation(3);
            uint8_t second = static_cast<uint8_t>(*std::next(it_));
            uint8_t third = static_cast<uint8_t>(*std::next(it_, 2));
            uint8_t fourth = static_cast<uint8_t>(*std::next(it_, 3));
            current_ = static_cast<char32_t>((first & 0x07) << 18) | ((second & 0x3F) << 12) | ((third & 0x3F) << 6) | (fourth & 0x3F);
            bytes_consumed_ = 4;
            // 检查码点是否在Unicode合法范围内
            if (current_ > 0x10FFFF) {
                throw std::invalid_argument("UTF-8序列超出Unicode范围");
            }
        } else {
            throw std::invalid_argument("无效的UTF-8起始字节");
        }
    }

    // 检查后续字节是否足够且合法
    void check_continuation(size_t count) {
        if (std::next(it_, count) > end_) {
            throw std::invalid_argument("截断的UTF-8序列");
        }
        for (size_t i = 1; i <= count; ++i) {
            uint8_t byte = static_cast<uint8_t>(*std::next(it_, i));
            if ((byte & 0xC0) != 0x80) {
                throw std::invalid_argument("无效的UTF-8续字节");
            }
        }
    }

    // 移动迭代器到下一个码点的起始位置
    void advance() {
        std::advance(it_, bytes_consumed_);
    }
};

// 范围适配器
struct utf8to32 {
    const std::string& str;

    explicit utf8to32(const std::string& s) : str(s) {}

    using iterator = utf8_to_32_iterator;

    iterator begin() const {
        return iterator(str.begin(), str.begin(), str.end());
    }
    iterator end() const {
        return iterator(str.end(), str.begin(), str.end());
    }
};

// 管道运算符重载
utf8to32 operator|(const std::string& str, utf8to32) {
    return utf8to32(str);
}

2. 使用自定义迭代器的示例

#include <iostream>

int main() {
    std::string utf8_str = u8"Hello 世界 🌍";
    try {
        auto range = utf8_str | utf8to32{};
        for (auto it = range.begin(); it != range.end(); ++it) {
            std::cout << "码点U+" << std::hex << static_cast<uint32_t>(*it) << std::dec
                      << " 起始字节位置: " << it->start_position() << std::endl;
        }
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
    }
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:05:54