string与unicode码点之间的转换

在 JavaScript 中,字符串使用了 UTF-16 编码,该编码使用一个 16 比特的编码单元来表示大部分常见的字符,使用两个代码单元表示不常用的字符。

在 JavaScript 中 unicode 是以十六进制代码外加转义字符 “\u” 来表示的字符串。

"\uxxxx"

使用 “\u” 来对一个 Unicode 来进行解码,获得字符串。

那么如何将一个字符串转为一个 unicode 码点。

String.prototype.charCodeAt(index)

charCodeAt 字符串的实例方法,返回 0 ~ 65535 之间的整数,表示给定索引处的 UTF-16 代码单元。如果 index,不是一个数值,就默认是0,如果超出范围,返回NaN。

index
index 转为数值,如果Number 转换中不是一个数字,默认 index是0.
如果 index 不在 0~str.length 范围,返回 NaN。

注意,charCodeAt 只能表示基础平面那的 unicode 码点,对于辅助平面内的码点是不能表示的。

所以我们为了表示出辅助平面的码点,我们需要借助代理对,使用高位+地位来构成一个真正的字符。但是,这会造成一个问题,就是我们获取的码点也是代理过去的码点,即 0xD800~0xDB000xDC00~0xDFFF 两个之间的码点。

那么我们怎么才能修复一下 charCodeAt,来让它支持出现 在辅助平面的码点并返回。

// 下面的代码是出现了未知的非基础平面的范围。
 
function fixedCharCodeAt (str, idx) {
    idx = idx || 0;  // 默认不存在会是0。
    var code = str.charCodeAt(idx); // 先获取 基础平面内的码点。
    var hi, low;  // 高位and地位。
    if (0xD800 <= code && code <= 0xDBFF){ // 高位区间
        hi = code;  
        low = str.charCodeAt(idx + 1);  // 代理对的第二个 低位。
        if (isNaN(low)) {  // index超出长度会返回 NaN
            throw 'High surrogate not followed by low surrogate in fixedCharCodeAt()';
        }
        return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
    }
    if (0xDC00 <= code && code <= 0xDFFF) {
        hi = str.charCodeAt(idx-1);
        low = code;
        return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;
    }
    return code;  // 超出了范围返回NaN,以及就是返回基础平面的码点。
}

fixedCharCodeAt ('\uD800\uDC00', 0); // 65536  基础平面是 0~65535之间
fixedCharCodeAt ('\uD800\uDC00', 1); // 65536

对于上面的 ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000,我们其实是利用了一个反推,在 Unicode 3.0 给出了辅助平面去求得基本平面映射的公式。Unicode 3.0公式,我们可以从基础平面公式 ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000 反推回去得到一个辅助平面的unicode码点。

那么如何将一个 unicode 码点转为一个字符串?

String.fromCharCode(num1,num2....)

fromCharCode 字符串静态方法,用于将 unicode 码点转为对应的字符串。

参数范围在 0 ~ 65535 之间,大于的不做检查,比如 0x12345 —> 0x2345,返回一个字符串。

String.fromCharCode(65, 66, 67);   // 返回 "ABC"
String.fromCharCode(0x2014);       // 返回 "—"
String.fromCharCode(0x12014);      // 也是返回 "—"; 数字 1 被剔除并忽略
String.fromCharCode(8212);         // 也是返回 "—"; 8212 是 0x2014 的十进制表示
String.fromCharCode(0xD83C, 0xDF03); // Code Point U+1F303 "Night with
String.fromCharCode(55356, 57091);   // Stars" == "\uD83C\uDF03"
 
String.fromCharCode(0xD834, 0xDF06, 0x61, 0xD834, 0xDF07); // "\uD834\uDF06a\uD834\uDF07"

String.fromCodePoint(num1, num2….)

fromCodePoint 字符串静态方法,用于将 unicode 码点转为对应的字符串。如果传入无效的 Unicode 编码,将会抛出一个 RangeError 的异常。

String.fromCodePoint(42);       // "*"
String.fromCodePoint(65, 90);   // "AZ"
String.fromCodePoint(0x404);    // "\u0404"
String.fromCodePoint(0x2F804);  // "\uD87E\uDC04"
String.fromCodePoint(194564);   // "\uD87E\uDC04"
String.fromCodePoint(0x1D306, 0x61, 0x1D307) // "\uD834\uDF06a\uD834\uDF07"
 
String.fromCodePoint('_');      // RangeError
String.fromCodePoint(Infinity); // RangeError
String.fromCodePoint(-1);       // RangeError
String.fromCodePoint(3.14);     // RangeError
String.fromCodePoint(3e-2);     // RangeError
String.fromCodePoint(NaN);      // RangeError

它和 String.fromCharCode 的区别就是 它可以识别辅助平面的码点,并返回指定的字符。

String.fromCodePoint(0x2F804)
// Polyfill: 它是ECMASscript 2015 新增的特性。
if (!String.fromCodePoint) (function(stringFromCharCode) {
    var fromCodePoint = function(_) {
        var codeUnits = [], codeLen = 0, result = "";
        for (var index=0, len = arguments.length; index !== len; ++index) {
            var codePoint = +argument[index]; // +会进行隐式强制转换,Number()转为一个数
            // `NaN`, `-Infinity`, `+Infinity`
            // 表示码点不会大于 辅助平面最大 0x10FFFF 并且 codePoint>>>0 小数和负数将会变为        
            // 另一个数,所以不会等于本身。
            if (!(codePoint < 0x10FFFF && (codePoint>>>0) === codePoint)) {
                throw RangeError("Invalid code point: " + codePoint);
            }
            if (codePoint <= 0xFFFF) { // BMP codeLen = codeUnits.push(codePoint); // 基础平面的码点 } else { codePoint -= 0x10000; codeLen = codeUnits.push( (codePoint >> 10) + 0xD800,  // 高位
                    (codePoint % 0x400) + 0xDC00 // 低位
                );
            }
            if (codeLen >= 0x3fff) {  // 参数超过 0x3fff,就不需要在寸了。
              result += stringFromCharCode.apply(null, codeUnits);
              codeUnits.length = 0;
            }
        }
        return result + stringFromCharCode.apply(null, codeUnits);
    }
    try {
      // IE 8支持下面这种添加静态属性。
      Object.defineProperty(String, "fromCodePoint", {
        "value": fromCodePoint, "configurable": true, "writable": true
      });
    } catch(e) {
      // 如果上面不成功,我们将直接赋值即可。
      String.fromCodePoint = fromCodePoint;
    }
})(String.fromCharCode);

关于有符号位移 >> 和 无符号位移 >>>,可以查看 位移

String.prototype.charAt(index)

charAt 方法可以将一个字符串返回指定的字符。 index 的范围是 0~length-1之间,如果不提供,默认使用 0。如果指定的 index 值超出了该范围,则返回一个空字符串。

var anyString = "Brave new world";
 
console.log("The character at index 0   is '" + anyString.charAt(0)   + "'");
console.log("The character at index 1   is '" + anyString.charAt(1)   + "'");
console.log("The character at index 2   is '" + anyString.charAt(2)   + "'");
console.log("The character at index 3   is '" + anyString.charAt(3)   + "'");
console.log("The character at index 4   is '" + anyString.charAt(4)   + "'");
console.log("The character at index 999 is '" + anyString.charAt(999) + "'");

我们在获取字符的长度时,会把辅助平面的看作是两个长度。

function getChar(str, i) {
    var code = str.charCodeAt(i);  // 先获取码点。
    if (isNaN(code)) {
        return ''; // 不在范围内,返回空字符.
    }
    if (code < 0xD800 || code > 0xDFFF) { // 本来就是基础平面的码点。
        return str.charAt(i);  // 直接返回字符
    }
    if (0xD800 <= code && code <= 0xDBFF) {. // 高位在范围内
        if (str.length <= (i+1)) { // 是否还存在低位 throw 'High surrogate without following low surrogate'; } var next = str.charCodeAt(i + 1); if (0xDC00 > next || next > 0xDFFF) { // 低位不在范围内
            throw 'High surrogate without following low surrogate';
        }
        return str.charAt(i) + str.charAt(i + 1); // 返回高位 + 低位
    }
    if (i === 0) {   // 第一位就是低位
        throw 'Low surrogate without preceding high surrogate';
    }
    var prev = str.charCodeAt(i - 1); // 高位
    if (0xD800 > prev || prev > 0xDBFF) {. // 高位不在范围
        throw 'Low surrogate without preceding high surrogate';
    }
    return false;  // 高位存在,低位也存在,低位的字符就不需要在返回出来,统一在高位返回。
}

比较:

charAt charCodeAt
参数Number转换,转换后不是一个数值,就使用默认值 0,获取第一个字符。 参数Number转换,转换后不是一个数值,就使用默认值 0,获取第一个字符。
返回值是一个字符,只要参数不是 0 ~length – 1,包括 Infinity 返回空字符。 返回值是一个字符,只要参数不是 0 ~length – 1,包括 Infinity 返回 NaN。
fromCharCode fromCodePoint
参数Number转换,转换后不是一个数值,就会返回 “\x00″。 参数Number转换,转换后不是一个数值,就会抛出异常 RangeError。

「点点赞赏,手留余香」

0

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:
1. 本站所有文章教程及资源素材均来源于网络与用户分享或为本站原创,仅限用于学习和研究。
2. 如果内容损害你的权益请联系客服QQ:1642748312给予处理。
码云笔记 » string与unicode码点之间的转换

发表评论

IT互联网行业相关广告投放 更专业 更精准

立即查看 联系我们