【C++/Java】16进制字符串表示的Unicode字符与UTF8互转

本文详细解析了Unicode与UTF8编码的关系,介绍了如何通过C++和Java代码实现Unicode十六进制字符串与UTF8编码之间的相互转换。

 

问题描述:要把类似 "\x4E00\x6735\x82B1\x1F339" 这样的字符串转换成UTF8编码的可见字符串 "一朵花🌹"

 

先简单说明一下Unicode与UTF8的关系:

Unicode标准可以理解为一个给世界上所有的字符统一编号的字典,一个字符的Unicode码就是一个编号,也仅仅是一个编号而已,并没有规定这个字符具体要怎么编码bit位来存储和发送。

而UTF8就是具体的编码规则,编码规则如下:

Unicode码范围
十六进制
标量值(scalar value)
二进制
UTF-8
二进制十六进制
注释
000000 - 00007F
128个代码
00000000 00000000 0zzzzzzz0zzzzzzz(00-7F)ASCII字符范围,字节由零开始
七个z七个z
000080 - 0007FF
1920个代码
00000000 00000yyy yyzzzzzz110yyyyy(C0-DF) 10zzzzzz(80-BF)第一个字节由110开始,接着的字节由10开始
三个y;二个y;六个z五个y;六个z
000800 - 00D7FF
00E000 - 00FFFF
61440个代码 [Note 1]
00000000 xxxxyyyy yyzzzzzz1110xxxx(E0-EF) 10yyyyyy 10zzzzzz第一个字节由1110开始,接着的字节由10开始
四个x;四个y;二个y;六个z四个x;六个y;六个z
010000 - 10FFFF
1048576个代码
000wwwxx xxxxyyyy yyzzzzzz11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz将由11110开始,接着的字节由10开始

有人可能会疑惑为什么不直接使用Unicode编号来存储和传送字符呢?

主要是因为区分开字符集和编码方案,可使字符的编码存储处理逻辑更清晰、更灵活。

由于Unicode编号只是个数字而已,具体来讲就是short(2字节的Unicode)或者int(4字节扩展Unicode),是定长的, 这对于编号在1字节内的那些常用字符(ASCII字符范围)来说是一种浪费,所以需要UTF8这样的变长编码来节省空间。另一方面,如果是为了更简便快速地进行字符串截断操作,则可考虑使用UTF32这样的定长(4字节)编码。

 

接下来直接上代码

 

C++11

Unicode转UTF8

#include <string>
#include <iostream>
#include <locale>
#include <codecvt>
#include <regex>
using namespace std;

/**
 * @description: Unicode码的16进制字符串组成的字符串转UTF8字符
 * @param src 例:\x4E00\x6735\x82B1\x1F339
 * @return UTF8编码的字符串,例:一朵花🌹
 */
string UniHexStrToUtf8(const string& src) {
    static regex re(R"(\\x([0-9a-zA-Z]{4,5}))");
    regex_token_iterator<string::const_iterator> rend;
    regex_token_iterator<string::const_iterator> it(src.begin(), src.end(), re, 1);
    wstring_convert<codecvt_utf8<char32_t>, char32_t> conv;
    string result;
    while (it!=rend) {
        result += conv.to_bytes( (char32_t)strtoul((*it).str().c_str(), NULL, 16) );
        ++it;
    }
    return result;
}

int main() {
    cout << UniHexStrToUtf8(R"(\x4E00\x6735\x82B1\x1F339)") << endl;
    return 0;
}

运行结果:

控制台里显示不了emoji,把结果复制粘贴到notepad++里,可以看出没有问题。

 

UTF8转Unicode

#include <string>
#include <iostream>
#include <locale>
#include <codecvt>
#include <iomanip>
using namespace std;

/**
 * @description: UTF8字符串转Unicode码的16进制字符串组成的字符串
 * @param src 例:一朵花🌹
 * @return Unicode码的16进制字符串组成的字符串,例:\x4E00\x6735\x82B1\x1F339
 */
string Utf8ToUniHexStr(const string& src) {
    wstring_convert<codecvt_utf8<char32_t>, char32_t> conv;
    u32string u32str = conv.from_bytes(src);
    stringstream result;
    for (char32_t ch : u32str) {
        result << "\\x" << hex << setw(4) << setfill('0') << ch;
    }
    return result.str();
}

int main() {
    cout << Utf8ToUniHexStr(u8"一朵花🌹") << endl;
    return 0;
}

运行结果:

 

JAVA (8+)

Unicode转UTF8 和 UTF8转Unicode

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class StringUtils {

  public static String UniHexStrToUtf8(String src) {
    Pattern r = Pattern.compile("\\\\x([0-9a-zA-Z]{4,5})");
    Matcher m = r.matcher(src);
    StringBuilder result = new StringBuilder();
    while (m.find()) {
      result.append(Character.toChars(Integer.parseInt(m.group(1), 16)));
    }
    return result.toString();
  }

  public static String Utf8ToUniHexStr(String src) {
    StringBuilder result = new StringBuilder();
    for (int ch : src.codePoints().toArray()) {
      result.append(String.format("\\x%04x", ch));
    }
    return result.toString();
  }

  public static void main(String[] args) {
    System.out.println(UniHexStrToUtf8("\\x4E00\\x6735\\x82B1\\x1F339"));
    System.out.println(Utf8ToUniHexStr("一朵花\uD83C\uDF39"));
  }
}

运行结果:

将带有 `\u` 义的 16 进制字符串换为汉字的 UTF-16 编码,可以通过编程语言中的内置功能来实现。以下以 Python 和 C++ 为例进行说明。 ### Python 实现 在 Python 中,可以使用 `encode` 和 `decode` 方法来处理 `\u` 义的 16 进制字符串,并将其换为 UTF-16 编码[^1]。 ```python # 原始字符串,包含 \u 义的 16 进制字符 hex_str = "\\u4f60\\u597d" # 表示 "你好" # 将字符串解码为 UTF-16 编码 utf16_bytes = hex_str.encode('utf-8').decode('unicode_escape').encode('utf-16') # 输出 UTF-16 编码的结果 print(utf16_bytes) ``` ### C++ 实现 在 C++ 中,可以使用 `std::wstring_convert` 和 `std::codecvt_utf8_utf16` 来处理 UTF-8UTF-16 之间的换。对于 `\u` 义的 16 进制字符串,首先需要解析这些字符,然后将其换为 UTF-16 编码[^2]。 ```cpp #include <iostream> #include <string> #include <sstream> #include <locale> #include <codecvt> std::wstring utf8_to_utf16(const std::string& str) { std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> converter; return converter.from_bytes(str); } int main() { // 原始字符串,包含 \u 义的 16 进制字符 std::string hex_str = "\\u4f60\\u597d"; // 表示 "你好" // 解析 \u 字符 std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> utf8_conv; std::string parsed_str = utf8_conv.to_bytes(utf8_conv.from_bytes(hex_str)); // 将解析后的字符串换为 UTF-16 编码 std::wstring utf16_str = utf8_to_utf16(parsed_str); // 输出 UTF-16 编码的结果 for (wchar_t wc : utf16_str) { std::wcout << std::hex << std::setw(4) << std::setfill(L'0') << static_cast<int>(wc) << " "; } std::wcout << std::endl; return 0; } ``` ### 总结 通过上述方法,可以将 `\u` 义的 16 进制字符串换为汉字的 UTF-16 编码。Python 提供了简洁的方法来实现这一功能,而 C++ 则需要更多的代码来处理义和编码换。
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值