function toFullUnicode(str) {
let result = '';
for (let i = 0; i < str.length; i++) {
const codePoint = str.codePointAt(i);
const hex = codePoint.toString(16).toUpperCase();
if (codePoint <= 0xFFFF) {
// 基本多文种平面 (BMP)
result += '\\u' + '0'.repeat(4 - hex.length) + hex;
} else {
// 辅助平面
result += '\\u{' + hex + '}';
i++; // 跳过代理对
}
}
return result;
}
console.log(toFullUnicode('😀中文')); // \u{1F600}\u4E2D\u6587
console.log("\u{1F600}\u4E2D\u6587")
中文转 Unicode 原理详解
1. 字符编码基础概念
1.1 字符集与编码
-
字符集 (Character Set): 字符的集合
-
编码 (Encoding): 将字符映射到二进制数据的规则
-
Unicode: 统一的字符集,为所有字符分配唯一编号(码点)
1.2 核心原理
中文字符 → Unicode码点 → 十六进制表示 → Unicode转义序列
"中" → U+4E2D → 0x4E2D → "\u4E2D"
2. Unicode 标准解析
2.1 Unicode 码点结构
Unicode 字符平面结构:
├── 基本多文种平面 (BMP, 0x0000-0xFFFF)
│ ├── ASCII (0x0000-0x007F)
│ ├── 拉丁字母扩展 (0x0080-0x00FF)
│ ├── 中文、日文、韩文 (CJK) (0x4E00-0x9FFF)
│ └── 其他语言
├── 辅助平面 (0x010000-0x10FFFF)
│ ├── 扩展汉字 (0x20000-0x2A6DF)
│ └── 表情符号 (0x1F600-0x1F64F)
2.2 中文字符的 Unicode 范围
常用中文字符分布:
1. 基本汉字 (CJK Unified Ideographs): U+4E00 - U+9FFF
- 包含 20,902 个汉字
- 示例:中(U+4E2D)、文(U+6587)
2. 扩展A区: U+3400 - U+4DBF
- 包含 6,582 个汉字
3. 扩展B区: U+20000 - U+2A6DF
- 包含 42,711 个汉字
4. 扩展C-F区: 更多生僻字
3. JavaScript 中的实现原理
3.1 核心方法原理
// 原理1: charCodeAt() 内部机制
function simulateCharCodeAt(str, index) {
// 实际实现步骤:
// 1. 获取字符的UTF-16编码单元
// 2. 返回0-65535之间的整数
// 3. 对于基本平面字符,直接返回码点
// 4. 对于辅助平面,返回代理对的高位或低位
const code = str.charCodeAt(index);
// JavaScript 使用 UTF-16 编码
// BMP 字符: 直接映射
// 辅助平面字符: 使用代理对 (surrogate pair)
return code;
}
3.2 UTF-16 代理对原理
// 辅助平面字符的编码方式
function encodeSurrogatePair(codePoint) {
// codePoint > 0xFFFF 的字符需要代理对
// 1. 减去 0x10000,得到20位值
const code = codePoint - 0x10000;
// 2. 分割为高10位和低10位
const high = (code >> 10) + 0xD800; // 高代理项: 0xD800-0xDBFF
const low = (code & 0x3FF) + 0xDC00; // 低代理项: 0xDC00-0xDFFF
return [high, low];
}
// 解码代理对
function decodeSurrogatePair(high, low) {
// 验证是否为有效代理对
if (high < 0xD800 || high > 0xDBFF) return null;
if (low < 0xDC00 || low > 0xDFFF) return null;
// 计算原始码点
return ((high - 0xD800) << 10) + (low - 0xDC00) + 0x10000;
}
4. 转换过程的底层原理
4.1 从字符到码点
// 底层实现步骤
function charToCodePoint(ch) {
// 步骤1: 获取UTF-16编码单元
const first = ch.charCodeAt(0);
// 步骤2: 检查是否为高代理项
if (first >= 0xD800 && first <= 0xDBFF) {
// 是代理对的高位
const second = ch.charCodeAt(1);
if (second >= 0xDC00 && second <= 0xDFFF) {
// 是有效的代理对
return decodeSurrogatePair(first, second);
}
}
// 步骤3: 直接返回(BMP字符)
return first;
}
4.2 码点到十六进制的转换
function codePointToHex(codePoint, padding = 4) {
// 码点转为十六进制
let hex = codePoint.toString(16).toUpperCase();
// 补零
if (hex.length < padding) {
hex = '0'.repeat(padding - hex.length) + hex;
}
return hex;
}
5. 不同格式的 Unicode 表示
5.1 各种格式的转换规则
// Unicode 转义序列的生成原理
function generateUnicodeEscape(codePoint, format) {
switch (format) {
case 'js':
// JavaScript 格式: \uXXXX 或 \u{XXXXXX}
if (codePoint <= 0xFFFF) {
return '\\u' + codePointToHex(codePoint, 4);
} else {
return '\\u{' + codePointToHex(codePoint) + '}';
}
case 'html':
// HTML 实体格式: &#XXXXX;
return '&#' + codePoint + ';';
case 'css':
// CSS 格式: \XXXXXX
return '\\' + codePointToHex(codePoint);
case 'url':
// URL 编码: %XX%XX%XX
if (codePoint <= 0xFF) {
return '%' + codePointToHex(codePoint, 2);
} else {
// UTF-8 编码
const bytes = codePointToUTF8(codePoint);
return bytes.map(b => '%' + b.toString(16).toUpperCase()).join('');
}
}
}
5.2 UTF-8 编码原理
// 码点到 UTF-8 的转换
function codePointToUTF8(codePoint) {
const bytes = [];
if (codePoint <= 0x7F) {
// 1字节: 0xxxxxxx
bytes.push(codePoint);
} else if (codePoint <= 0x7FF) {
// 2字节: 110xxxxx 10xxxxxx
bytes.push(0xC0 | (codePoint >> 6));
bytes.push(0x80 | (codePoint & 0x3F));
} else if (codePoint <= 0xFFFF) {
// 3字节: 1110xxxx 10xxxxxx 10xxxxxx
bytes.push(0xE0 | (codePoint >> 12));
bytes.push(0x80 | ((codePoint >> 6) & 0x3F));
bytes.push(0x80 | (codePoint & 0x3F));
} else if (codePoint <= 0x10FFFF) {
// 4字节: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
bytes.push(0xF0 | (codePoint >> 18));
bytes.push(0x80 | ((codePoint >> 12) & 0x3F));
bytes.push(0x80 | ((codePoint >> 6) & 0x3F));
bytes.push(0x80 | (codePoint & 0x3F));
}
return bytes;
}
6. JavaScript 字符串内部表示
6.1 字符串的内存结构
// JavaScript 字符串的底层存储
const str = "中文";
// 内存中实际存储的是UTF-16编码
// "中": UTF-16编码: 0x4E2D
// "文": UTF-16编码: 0x6587
// 获取二进制表示
function getStringBinary(str) {
const result = [];
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
result.push(`0x${code.toString(16).toUpperCase()}`);
}
return result;
}
console.log(getStringBinary("中文")); // ["0x4E2D", "0x6587"]
6.2 代理对的特殊处理
// 处理包含表情符号的字符串
const emoji = "😀中文";
console.log(emoji.length); // 4(😀占2个代码单元)
// 正确获取码点的方法
function getCodePoints(str) {
const codePoints = [];
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
if (code >= 0xD800 && code <= 0xDBFF) {
// 高代理项
const next = str.charCodeAt(i + 1);
if (next >= 0xDC00 && next <= 0xDFFF) {
// 有效的代理对
const codePoint = decodeSurrogatePair(code, next);
codePoints.push(codePoint);
i++; // 跳过低位
continue;
}
}
codePoints.push(code);
}
return codePoints;
}
console.log(getCodePoints("😀中文"));
// [128512, 20013, 25991]
7. 转换算法的优化原理
7.1 查找表优化
// 使用查找表加速常用字符的转换
class UnicodeConverter {
constructor() {
// 预计算常用字符的Unicode转义序列
this.cache = new Map();
this.commonChinese = new Set();
// 预加载常用汉字
for (let i = 0x4E00; i <= 0x9FA5; i++) {
this.commonChinese.add(String.fromCharCode(i));
}
}
toUnicode(str) {
let result = '';
for (let i = 0; i < str.length; i++) {
const char = str[i];
// 检查缓存
if (this.cache.has(char)) {
result += this.cache.get(char);
continue;
}
// 计算Unicode转义序列
const code = char.codePointAt(0);
let unicodeSeq;
if (code <= 0xFFFF) {
unicodeSeq = '\\u' + code.toString(16).toUpperCase().padStart(4, '0');
} else {
unicodeSeq = '\\u{' + code.toString(16).toUpperCase() + '}';
}
// 缓存结果
this.cache.set(char, unicodeSeq);
result += unicodeSeq;
}
return result;
}
}
7.2 正则表达式优化原理
// 正则表达式的优化版本
function optimizedToUnicode(str) {
// 使用正则一次性匹配所有非ASCII字符
return str.replace(/[^\x00-\x7F]/gu, (char) => {
const code = char.codePointAt(0);
// 快速判断是否需要代理对
if (code <= 0xFFFF) {
return '\\u' + code.toString(16).toUpperCase().padStart(4, '0');
} else {
return '\\u{' + code.toString(16).toUpperCase() + '}';
}
});
}
8. 与其他编码的关系
8.1 Unicode 与 GB2312/GBK
// Unicode 与 GBK 的映射原理
const gbkToUnicode = {
// 示例:中文字符映射
"0xB0A1": 0x554A, // "啊"
"0xB0A2": 0x963F, // "阿"
// ... 实际是庞大的映射表
};
// 转换过程
function gbkToUnicodeChar(gbkCode) {
const unicodeCode = gbkToUnicode[gbkCode];
if (unicodeCode) {
return String.fromCharCode(unicodeCode);
}
return '?';
}
8.2 字节顺序标记 (BOM)
// BOM 对 Unicode 编码的影响
function detectEncodingWithBOM(buffer) {
if (buffer.length >= 2) {
const bom = buffer.readUInt16BE(0);
switch (bom) {
case 0xFEFF: // UTF-16 BE
return { encoding: 'utf16-be', hasBOM: true };
case 0xFFFE: // UTF-16 LE
return { encoding: 'utf16-le', hasBOM: true };
}
}
if (buffer.length >= 3) {
if (buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) {
return { encoding: 'utf8', hasBOM: true };
}
}
return { encoding: 'unknown', hasBOM: false };
}
9. 实际应用原理
9.1 网络传输中的编码
// HTTP 请求中的编码处理
function prepareRequestData(data) {
// 原理:将中文字符转为百分比编码
return encodeURIComponent(data)
.replace(/%/g, '\\x')
.replace(/!/g, '%21')
.replace(/'/g, '%27')
.replace(/\(/g, '%28')
.replace(/\)/g, '%29')
.replace(/\*/g, '%2A');
}
9.2 数据库存储
// 数据库中的Unicode存储原理
function encodeForDatabase(text) {
// 1. 检测是否包含4字节字符(辅助平面)
const hasSupplementary = /[\uD800-\uDBFF][\uDC00-\uDFFF]/.test(text);
if (hasSupplementary) {
// 需要UTF8mb4编码
return {
encoding: 'utf8mb4',
data: text
};
} else {
// 可以使用UTF-8
return {
encoding: 'utf8',
data: text
};
}
}
10. 性能考虑
10.1 时间复杂度分析
// 不同算法的时间复杂度
class UnicodePerformance {
// 方法1: 简单循环 - O(n)
simpleConvert(str) {
let result = '';
for (let i = 0; i < str.length; i++) {
const code = str.charCodeAt(i);
result += '\\u' + code.toString(16).padStart(4, '0');
}
return result;
}
// 方法2: 正则替换 - O(n) 但常数更小
regexConvert(str) {
return str.replace(/./g, (char) => {
return '\\u' + char.charCodeAt(0).toString(16).padStart(4, '0');
});
}
// 方法3: 数组拼接 - 更高效
arrayConvert(str) {
const parts = [];
for (let i = 0; i < str.length; i++) {
parts.push('\\u' + str.charCodeAt(i).toString(16).padStart(4, '0'));
}
return parts.join('');
}
}
关键原理总结:
-
编码层次:字符 → Unicode码点 → 二进制表示 → 转义序列
-
代理对机制:辅助平面字符通过2个UTF-16代码单元表示
-
格式差异:JavaScript、HTML、URL各有不同的转义格式
-
性能优化:缓存、正则、数组拼接等优化技术
-
兼容性:处理BOM、代理对、编码检测等问题