Javascript: Unicode字符串转换为十六进制

92

我正在尝试在JavaScript中将Unicode字符串转换为十六进制表示。

这是我所拥有的:

function convertFromHex(hex) {
    var hex = hex.toString();//force conversion
    var str = '';
    for (var i = 0; i < hex.length; i += 2)
        str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
    return str;
}

function convertToHex(str) {
    var hex = '';
    for(var i=0;i<str.length;i++) {
        hex += ''+str.charCodeAt(i).toString(16);
    }
    return hex;
}

但是它在Unicode字符上失败,比如中文;

输入: 漢字

输出: ªo"[W

有什么想法吗?这可以用JavaScript实现吗?

8个回答

122

请记住,JavaScript 代码单元的宽度为16位。因此,十六进制字符串形式将每个代码单元为4位。

用法:

var str = "\u6f22\u5b57"; // "\u6f22\u5b57" === "漢字"
alert(str.hexEncode().hexDecode());

将字符串转换为十六进制格式:

String.prototype.hexEncode = function(){
    var hex, i;

    var result = "";
    for (i=0; i<this.length; i++) {
        hex = this.charCodeAt(i).toString(16);
        result += ("000"+hex).slice(-4);
    }

    return result
}

再次回来:

String.prototype.hexDecode = function(){
    var j;
    var hexes = this.match(/.{1,4}/g) || [];
    var back = "";
    for(j = 0; j<hexes.length; j++) {
        back += String.fromCharCode(parseInt(hexes[j], 16));
    }

    return back;
}

1
谢谢,只有一个问题(可能很蠢..) - 在javascript中如何从“汉字”获取\u6f22\u5b57?最接近的方法是使用escape()函数,但这会使用% - 我猜可以使用某种正则表达式将%替换为/ - 但escape()函数也已被弃用。EncodeURI和encodeURIComponent都会给出不同的输出。有什么想法吗? - user429620
2
"\u6f22\u5b57" 是字面值 "漢字" 的 Unicode 转义形式,就像 \n 是换行符一样。我倾向于使用它们来避免歧义和字符编码问题。有关详细信息,请参见规范。要自己生成它们,请将上面的 ("000"+hex).slice(-4) 更改为 "\\u" + ("000"+hex).slice(-4)。表达式 "\u6f22\u5b57" === "漢字" 的求值结果为 true,因为在代码解析后它们是相同的。 - McDowell
2
如果你按照顶部算法编写,“test”编码为“0074006500730074”。这里没有ASCII。JavaScript字符串__始终__是UTF-16。 - McDowell
1
我修复了hexDecode函数,因为它似乎无法正常工作:var a = "\\x73\\x75\\x62\\x73\\x74\\x72"; var str = "\\u6f22\\u5b57"; String.prototype.hexDecode = function(){ var j; var hexes = this.split("\\"); var back = ""; for(j = 1; j - martian17
1
在定义某些函数的使用方法之前,将其放置在代码中可能会令人困惑。我假设JS本身已经具备了这些功能。 - user3064538
显示剩余6条评论

30

这是对麦克道尔算法的微调,它不会对结果进行填充:

  function toHex(str) {
    var result = '';
    for (var i=0; i<str.length; i++) {
      result += str.charCodeAt(i).toString(16);
    }
    return result;
  }

8
为什么不想填充呢?现在十六进制输出是不明确的。 - Inverse
1
如果您需要对字符串进行十六进制编码或类似操作。 - redgeoff
不确定我在看什么,但这对我获取用户的私人CouchDB数据库很有用!谢谢 - kyw
恭喜 @redgeoff!当将字符串传递到 PHP 并使用 hex2bin() 解码时,此解决方案有效。 - JMerinoH

19

一个更现代的编码解决方案:

// This is the same for all of the below, and
// you probably won't need it except for debugging
// in most cases.
function bytesToHex(bytes) {
  return Array.from(
    bytes,
    byte => byte.toString(16).padStart(2, "0")
  ).join("");
}

// You almost certainly want UTF-8, which is
// now natively supported:
function stringToUTF8Bytes(string) {
  return new TextEncoder().encode(string);
}

// But you might want UTF-16 for some reason.
// .charCodeAt(index) will return the underlying
// UTF-16 code-units (not code-points!), so you
// just need to format them in whichever endian order you want.
function stringToUTF16Bytes(string, littleEndian) {
  const bytes = new Uint8Array(string.length * 2);
  // Using DataView is the only way to get a specific
  // endianness.
  const view = new DataView(bytes.buffer);
  for (let i = 0; i != string.length; i++) {
    view.setUint16(i, string.charCodeAt(i), littleEndian);
  }
  return bytes;
}

// And you might want UTF-32 in even weirder cases.
// Fortunately, iterating a string gives the code
// points, which are identical to the UTF-32 encoding,
// though you still have the endianess issue.
function stringToUTF32Bytes(string, littleEndian) {
  const codepoints = Array.from(string, c => c.codePointAt(0));
  const bytes = new Uint8Array(codepoints.length * 4);
  // Using DataView is the only way to get a specific
  // endianness.
  const view = new DataView(bytes.buffer);
  for (let i = 0; i != codepoints.length; i++) {
    view.setUint32(i, codepoints[i], littleEndian);
  }
  return bytes;
}

示例:

bytesToHex(stringToUTF8Bytes("hello 漢字 "))
// "68656c6c6f20e6bca2e5ad9720f09f918d"
bytesToHex(stringToUTF16Bytes("hello 漢字 ", false))
// "00680065006c006c006f00206f225b570020d83ddc4d"
bytesToHex(stringToUTF16Bytes("hello 漢字 ", true))
// "680065006c006c006f002000226f575b20003dd84ddc"
bytesToHex(stringToUTF32Bytes("hello 漢字 ", false))
// "00000068000000650000006c0000006c0000006f0000002000006f2200005b57000000200001f44d"
bytesToHex(stringToUTF32Bytes("hello 漢字 ", true))
// "68000000650000006c0000006c0000006f00000020000000226f0000575b0000200000004df40100"

对于解码来说,通常要简单得多,你只需要:

function hexToBytes(hex) {
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i !== bytes.length; i++) {
        bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
    }
    return bytes;
}

然后使用TextDecoder的编码参数:

// UTF-8 is default
new TextDecoder().decode(hexToBytes("68656c6c6f20e6bca2e5ad9720f09f918d"));
// but you can also use:
new TextDecoder("UTF-16LE").decode(hexToBytes("680065006c006c006f002000226f575b20003dd84ddc"))
new TextDecoder("UTF-16BE").decode(hexToBytes("00680065006c006c006f00206f225b570020d83ddc4d"));
// "hello 漢字 "

以下是允许使用的编码名称列表:https://www.w3.org/TR/encoding/#names-and-labels
你可能会注意到UTF-32不在该列表中,这很麻烦,因此:
function bytesToStringUTF32(bytes, littleEndian) {
  const view = new DataView(bytes.buffer);
  const codepoints = new Uint32Array(view.byteLength / 4);
  for (let i = 0; i !== codepoints.length; i++) {
    codepoints[i] = view.getUint32(i * 4, littleEndian);
  }
  return String.fromCodePoint(...codepoints);
}

然后:

bytesToStringUTF32(hexToBytes("00000068000000650000006c0000006c0000006f0000002000006f2200005b57000000200001f44d"), false)
bytesToStringUTF32(hexToBytes("68000000650000006c0000006c0000006f00000020000000226f0000575b0000200000004df40100"), true)
// "hello 漢字 "

谢谢!我找了两个小时,结果只需要这个:function stringToUTF8Bytes(string) { return new TextEncoder().encode(string); } - Jon R

17

取决于你使用的编码方式。如果你想将UTF-8编码的十六进制转换为字符串,请使用以下方法:

function fromHex(hex,str){
  try{
    str = decodeURIComponent(hex.replace(/(..)/g,'%$1'))
  }
  catch(e){
    str = hex
    console.log('invalid hex input: ' + hex)
  }
  return str
}

对于另一个方向,请使用以下内容:

function toHex(str,hex){
  try{
    hex = unescape(encodeURIComponent(str))
    .split('').map(function(v){
      return v.charCodeAt(0).toString(16)
    }).join('')
  }
  catch(e){
    hex = str
    console.log('invalid text input: ' + str)
  }
  return hex
}

对于toHex函数,如果hex < 10,则需要进行'0'填充。如果文本中出现\n或\t,则它们将显示为'9'或'a'。但实际上应该分别显示为'09'和'0a'。 - Munawwar
返回 v.charCodeAt(0).toString(16).padStart(2, '0') - Munawwar

8

如何在JavaScript中从漢字得到"\u6f22\u5b57"

这些是JavaScript Unicode转义序列,例如\u12AB。要进行转换,可以迭代字符串中的每个代码单元,在其上调用.toString(16),然后从那里开始。

但是,更有效的方法是在输出中尽可能使用十六进制转义序列,例如\xAA

还要注意,ASCII符号(如Ab-)可能不需要转义。

我编写了一个小型的JavaScript库,可以为您完成所有这些操作,名为jsesc。它有很多选项来控制输出。

以下是该工具的在线演示:http://mothereff.in/js-escapes#1%E6%BC%A2%E5%AD%97


你的问题被标记为utf-8。阅读你的问题剩余部分,似乎UTF-8编码/解码并不是你想要的,但以防万一,如果你需要它:使用utf8.js(在线演示)。

8

这是给你的。 :D

"漢字".split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(4,"0"),"")
"6f225b57"

对于非Unicode编码

"hi".split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(2,"0"),"")
"6869"

将ASCII(utf-8)二进制HEX字符串转换为字符串

"68656c6c6f20776f726c6421".match(/.{1,2}/g).reduce((acc,char)=>acc+String.fromCharCode(parseInt(char, 16)),"")

将字符串转换为ASCII (utf-8) 二进制HEX字符串

"hello world!".split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(2,"0"),"")

--- Unicode ---

将字符串转换为UNICODE(utf-16)二进制HEX字符串

"hello world!".split("").reduce((hex,c)=>hex+=c.charCodeAt(0).toString(16).padStart(4,"0"),"")

UTF-16二进制HEX字符串转换为字符串
"00680065006c006c006f00200077006f0072006c00640021".match(/.{1,4}/g).reduce((acc,char)=>acc+String.fromCharCode(parseInt(char, 16)),"")

3

UTF-8 支持的转换

解码

function utf8ToHex(str) {
  return Array.from(str).map(c => 
    c.charCodeAt(0) < 128 ? c.charCodeAt(0).toString(16) : 
    encodeURIComponent(c).replace(/\%/g,'').toLowerCase()
  ).join('');
}

编码

function hexToUtf8(hex) {
  return decodeURIComponent('%' + hex.match(/.{1,2}/g).join('%'));
}

3
这是我的理解:这些函数将UTF8字符串转换为适当的HEX格式而不需要额外的零填充。一个真正的UTF8字符串由长度为1、2、3和4个字节的字符组成。
在处理这些内容时,我发现了几个关键问题:
  1. str.split('')不能正确地处理多字节字符(如表情符号)。处理这种情况的适当/现代方式是使用Array.from(str)
  2. encodeURIComponent()decodeURIComponent()是很好的工具,可以在字符串和十六进制之间进行转换。它们是相当标准的,可以正确地处理UTF8。
  3. (大多数)ASCII字符(代码0-127)不会被URI编码,因此需要单独处理。但对于这些字符,c.charCodeAt(0).toString(16)非常有效。
    function utf8ToHex(str) {
      return Array.from(str).map(c => 
        c.charCodeAt(0) < 128 ? c.charCodeAt(0).toString(16) : 
        encodeURIComponent(c).replace(/\%/g,'').toLowerCase()
      ).join('');
    },
    function hexToUtf8: function(hex) {
      return decodeURIComponent('%' + hex.match(/.{1,2}/g).join('%'));
    }

演示: https://jsfiddle.net/lyquix/k2tjbrvq/


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