Java中的String.getBytes("UTF8")与JavaScript的类似功能

4

字节转字符串和反向转换

在文章中编写的函数是可以正常工作的,也就是说 pack(unpack("string")) 的结果是 "string"。但我希望能够得到与 Java 中 "string".getBytes("UTF8") 相同的结果。

问题在于如何编写一个与 Java 中 getBytes("UTF8") 具有相同功能的 JavaScript 函数?

对于拉丁字符串,上述文章中的 unpack(str) 提供了与 getBytes("UTF8") 相同的结果,除了它会在奇数位置添加 0 。但是对于非拉丁字符串,它的工作方式完全不同。是否有一种方法可以在 JavaScript 中像 Java 一样处理字符串数据呢?


也许是关于从JavaScript字符串中读取字节的问题? - Whymarrh
不对... "中".getBytes("UTF8") 得到的是 {-28, -72, -83},但是答案中的函数却得到了 [78, 45] - ivkremer
2
JavaScript使用UTF-16,因此0是每个16位代码单元的上半部分。当按照UTF-8方案编码时,那个汉字字符需要3个字节,而通过UTF-16只需要2个字节。 - obataku
4个回答

8

您无需编写完整的UTF-8编码器;有一种更简单的JS习语可以将Unicode字符串转换为表示UTF-8代码单元的字节串:

unescape(encodeURIComponent(str))

这是因为escape/unescape所使用的奇怪编码方式使用%xx十六进制序列来表示具有该代码的ISO-8859-1字符,而不是URI-component转义所使用的UTF-8。同样地,decodeURIComponent(escape(bytes))则相反。

因此,如果您想要一个数组,那么它应该是这样的:

function toUTF8Array(str) {
    var utf8= unescape(encodeURIComponent(str));
    var arr= new Array(utf8.length);
    for (var i= 0; i<utf8.length; i++)
        arr[i]= utf8.charCodeAt(i);
    return arr;
}

3
您可以使用这个函数(gist):
function toUTF8Array(str) {
    var utf8 = [];
    for (var i=0; i < str.length; i++) {
        var charcode = str.charCodeAt(i);
        if (charcode < 0x80) utf8.push(charcode);
        else if (charcode < 0x800) {
            utf8.push(0xc0 | (charcode >> 6), 
                      0x80 | (charcode & 0x3f));
        }
        else if (charcode < 0xd800 || charcode >= 0xe000) {
            utf8.push(0xe0 | (charcode >> 12), 
                      0x80 | ((charcode>>6) & 0x3f), 
                      0x80 | (charcode & 0x3f));
        }
        else {
            // let's keep things simple and only handle chars up to U+FFFF...
            utf8.push(0xef, 0xbf, 0xbd); // U+FFFE "replacement character"
        }
    }
    return utf8;
}

使用示例:

>>> toUTF8Array("中€")
[228, 184, 173, 226, 130, 172]

如果你希望像Java的字节转整数一样,对于大于127的值使用负数,那么你需要调整常量并使用

            utf8.push(0xffffffc0 | (charcode >> 6), 
                      0xffffff80 | (charcode & 0x3f));

并且

            utf8.push(0xffffffe0 | (charcode >> 12), 
                      0xffffff80 | ((charcode>>6) & 0x3f), 
                      0xffffff80 | (charcode & 0x3f));

最佳答案,干得好。同时还有优雅的代码。我将这个方法缩小了,并在toUTF8Array中添加了一个反向方法,最后将它们全部放入String.prototype,这使得使用变得非常清晰且简单。请查看:JavaScript-Bitwise-Shenanigans @GitHub - user257319

3

TextEncoder编码标准 的一部分,根据 Chromium Dashboard 上的 编码 API 条目,它已经在 Firefox 中发布并将在 Chrome 38 中发布。其他浏览器也有可用的 text-encoding polyfill。

以下 JavaScript 代码示例返回一个填充了您期望值的 Uint8Array

(new TextEncoder()).encode("string") 
// [115, 116, 114, 105, 110, 103]

一个更有趣的例子,更好地展示了UTF-8如何用îñ替换string中的in

(new TextEncoder()).encode("strîñg")
[115, 116, 114, 195, 174, 195, 177, 103]

1
这似乎比被接受的位操作自定义实现更好,后者是被接受的答案。 - masterxilo

0
以下函数将处理那些超过U+FFFF的字符。
因为JavaScript文本采用UTF-16编码,所以在字符串中使用两个“字符”来表示BMP之上的字符,并且charCodeAt返回相应的代理代码。fixedCharCodeAt可以处理这种情况。
function encodeTextToUtf8(text) {
    var bin = [];
    for (var i = 0; i < text.length; i++) {
        var v = fixedCharCodeAt(text, i);
        if (v === false) continue;
        encodeCharCodeToUtf8(v, bin);
    }
    return bin;
}

function encodeCharCodeToUtf8(codePt, bin) {
    if (codePt <= 0x7F) {
        bin.push(codePt);
    } else if (codePt <= 0x7FF) {
        bin.push(192 | (codePt >> 6), 128 | (codePt & 63));
    } else if (codePt <= 0xFFFF) {
        bin.push(224 | (codePt >> 12),
            128 | ((codePt >> 6) & 63),
            128 | (codePt & 63));
    } else if (codePt <= 0x1FFFFF) {
        bin.push(240 | (codePt >> 18),
            128 | ((codePt >> 12) & 63), 
            128 | ((codePt >> 6) & 63),
            128 | (codePt & 63));
    }
}

function fixedCharCodeAt (str, idx) {  
    // ex. fixedCharCodeAt ('\uD800\uDC00', 0); // 65536  
    // ex. fixedCharCodeAt ('\uD800\uDC00', 1); // 65536  
    idx = idx || 0;  
    var code = str.charCodeAt(idx);  
    var hi, low;  
    if (0xD800 <= code && code <= 0xDBFF) { // High surrogate (could change last hex to 0xDB7F to treat high private surrogates as single characters)  
        hi = code;  
        low = str.charCodeAt(idx+1);  
        if (isNaN(low)) {  
            throw(encoding_error.invalid_surrogate_pair.replace('%pos%', idx));
        }  
        return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;  
    }  
    if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate  
        // We return false to allow loops to skip this iteration since should have already handled high surrogate above in the previous iteration  
        return false;  
        /*hi = str.charCodeAt(idx-1); 
          low = code; 
          return ((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;*/  
    }  
    return code;  
}  

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接