【C++/Java】16进制字符串表示的Unicode字符与UTF8互转
程序员文章站
2022-07-15 13:09:57
...
问题描述:要把类似 "\x4E00\x6735\x82B1\x1F339" 这样的字符串转换成UTF8编码的可见字符串 "一朵花????"
先简单说明一下Unicode与UTF8的关系:
Unicode标准可以理解为一个给世界上所有的符号统一编号的字典,一个符号的Unicode码就是一个编号,也仅仅是一个编号而已,并没有规定这个符号具体要怎么存储和发送。
而UTF8就是具体的编码规则,编码规则如下:
Unicode码范围 十六进制 |
标量值(scalar value) 二进制 |
UTF-8 二进制/十六进制 |
注释 |
---|---|---|---|
000000 - 00007F 128个代码 |
00000000 00000000 0zzzzzzz | 0zzzzzzz(00-7F) | ASCII字符范围,字节由零开始 |
七个z | 七个z | ||
000080 - 0007FF 1920个代码 |
00000000 00000yyy yyzzzzzz | 110yyyyy(C0-DF) 10zzzzzz(80-BF) | 第一个字节由110开始,接着的字节由10开始 |
三个y;二个y;六个z | 五个y;六个z | ||
000800 - 00D7FF 00E000 - 00FFFF 61440个代码 [Note 1] |
00000000 xxxxyyyy yyzzzzzz | 1110xxxx(E0-EF) 10yyyyyy 10zzzzzz | 第一个字节由1110开始,接着的字节由10开始 |
四个x;四个y;二个y;六个z | 四个x;六个y;六个z | ||
010000 - 10FFFF 1048576个代码 |
000wwwxx xxxxyyyy yyzzzzzz | 11110www(F0-F7) 10xxxxxx 10yyyyyy 10zzzzzz | 将由11110开始,接着的字节由10开始 |
有人可能会疑惑为什么不直接使用Unicode编号来存储和传送字符呢?因为Unicode编号只是个数字而已,具体来讲就是short(2字节的Unicode)或者int(4字节扩展Unicode),是定长的, 这对于编号在1字节内的那些常用字符(ASCII字符范围)来说是一种浪费,所以才需要UTF8这样的变长编码来节省空间。
接下来直接上代码
C++11
Unicode转UTF8
#include <string>
#include <iostream>
#include <locale>
#include <codecvt>
#include <regex>
using namespace std;
/**
* @description: 单个Unicode码的16进制字符串转UTF8字符
* @param hex 单个Unicode码的16进制字符串,例:1F339
* @return UTF8编码的字符,例:????
*/
string UniHexToUtf8(const string& hex) {
uint32_t ch = strtoul(hex.c_str(), NULL, 16);
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
return conv.to_bytes( (char32_t)ch );
}
/**
* @description: Unicode码的16进制字符串组成的字符串转UTF8字符
* @param src 例:\x4E00\x6735\x82B1\x1F339
* @return UTF8编码的字符串,例:一朵花????
*/
string UniHexStrToUtf8(const string& src) {
static std::regex re(R"(\\x([0-9a-zA-Z]{4,5}))");
std::regex_token_iterator<std::string::const_iterator> rend;
regex_token_iterator<string::const_iterator> it(src.begin(), src.end(), re, 1);
string result;
while (it!=rend) {
result += UniHexToUtf8(*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) {
std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> conv;
std::u32string u32str = conv.from_bytes(src);
std::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"));
}
}
运行结果:
下一篇: Flask上传文件