【问题标题】:Robust conversion of hexadecimal string to byte values in JavaScriptJavaScript 中十六进制字符串到字节值的稳健转换
【发布时间】:2017-01-16 13:20:24
【问题描述】:

我尝试从包含十六进制字节表示的字符串中提取字节值。该字符串还包含(未知)需要忽略的非十六进制字符(分隔符、空格格式)。

给定输入字符串"f5 df 45:f8 a 8 f53",结果将是数组[245, 223, 69, 248, 168, 245]。请注意,字节值仅从两个十六进制数字输出(因此,最后一个 3 被忽略)。

作为附加约束,代码需要在 ecmascript 3 环境中工作。

到目前为止,我一直使用这种方法:

function parseHex(hex){
    hex = hex.replace(/[^0-9a-fA-F]/g, '');
    var i, 
        len = hex.length, 
        bin = [];
    for(i = 0; i < len - 1; i += 2){
        bin.push(+('0x' + hex.substring(i, i + 2)));
    }
    return bin;
}

但是,我觉得有可能找到一个更优雅的解决方案,所以问题是:

是否有更好的解决方案来解决这个问题(性能更好或用更少的代码解决问题)?

【问题讨论】:

  • 在您的帖子中找不到任何问题。
  • @ceving,试图澄清这个问题。
  • @ceving,试图研究是否应该将这个问题发布到 codereview,但我还不相信它对 SO 来说是题外话。这个guide to code review for SO users 特别建议:“请不要以‘它属于代码审查’的自定义理由投票结束”

标签: javascript hex byte converter


【解决方案1】:

更新答案 (ES3)

由于您在对我原始答案的评论中提到您仅限于 ES3,因此您应该能够做到这一点:

function parseHex(string) {
  // remove all non-hex characters, and then separate them into an array in groups of 2 characters
  var arr = string.replace(/[^0-9a-fA-F]/g, '').match(/[0-9a-fA-F]{2}/g);

  // mutate the array in-place with the correct decimal values
  for(var i = 0; i<arr.length; i++) {
    arr[i] = parseInt(arr[i], 16);
  }

  return arr;
}

parseHex('f5 df 45:f8 a 8 f53'); // => [245, 223, 69, 248, 168, 245]

它基本上会做map 所做的事情,除了它比map 具有更少的空间复杂度,因为它正在就地改变数组。请参阅updated jsfiddle

上一个答案(ES5)

你可以这样做(这里是jsbin example):

'f5 df 45:f8 a 8 f53'.replace(/[^0-9a-fA-F]/g, '').match(/[0-9a-fA-F]{2}/g).map(function(hex) {
  return parseInt(hex, 16);
});

// => [245, 223, 69, 248, 168, 245]

你可以把它做成这样的函数:

function parseHex(string) {
  return string.replace(/[^0-9a-fA-F]/g, '').match(/[0-9a-fA-F]{2}/g).map(function(hex) {
    return parseInt(hex, 16);
  });
}

parseHex('f5 df 45:f8 a 8 f53');

基本上您从字符串中删除非十六进制字符,然后匹配两个十六进制字符组(根据您的要求)。 This answer 描述了parseInt(hex, 16) 部分(相反的部分是hex.toString(16))。

【讨论】:

  • 这是一个不错的单行。不幸的是,使用.map() 不是我的选择(ES3)。
  • 感谢您让我注意到使用 .match() 并就地改变数组。
  • 当然,效果很好。您对.match() 的使用使我探索了几种正则表达式方法,但后来我发现了一种我更喜欢的非正则表达式方法。
【解决方案2】:

TL;DR

使用正则表达式方法可以减少代码,但性能更差。非正则表达式解决方案可提供更好的性能,但代价是稍微多一些代码。

正则表达式方法

经过更多研究/谷歌搜索(并看到 Josh Beams answer 使用 .match()),我认为有几种可能的正则表达式方法可以改进原始方法。

直接使用.match()(不使用.replace()),灵感来自Josh Beams answer

function parseHex(hex){
    hex = hex.match(/[\da-f]/gi);
    for(var i = 0; i < hex.length - 1; i += 2){
        hex[i >> 1] = +('0x' + hex[i] + hex[i + 1]);
    }
    hex.length = i >> 1;
    return hex;
}

使用.replace() 进行迭代(灵感来自this):

function parseHex(hex){
    var bin = [];
    hex.replace(/([\da-f])[^\da-f]*([\da-f])/gi,
        function(m, digit1, digit2){
            bin.push(+('0x' + digit1 + digit2));
        }
    );
    return bin;
}

循环使用.exec()(也受到this 的启发):

function parseHex(hex){
    var bin = [],
        regex = /([\da-f])[^\da-f]*([\da-f])/gi,
        result;
    while(result = regex.exec(hex)){
        bin.push(+('0x' + result[1] + result[2]));
    }
    return bin;
}

性能和非正则表达式解决方案

running performance tests here 之后,似乎没有一个正则表达式方法的性能明显优于原始方法。出于好奇,我尝试了一个非正则表达式的解决方案,它明显优于其他方法(以稍微多一点的代码为代价):

function parseHex(hex){
    var bin = [], i, c, isEmpty = 1, buffer;
    for(i = 0; i < hex.length; i++){
        c = hex.charCodeAt(i);
        if(c > 47 && c < 58 || c > 64 && c < 71 || c > 96 && c < 103){
            buffer = buffer << 4 ^ (c > 64 ? c + 9 : c) & 15;
            if(isEmpty ^= 1){
                bin.push(buffer & 0xff);
            }
        }
    }
    return bin;
}

我可能会选择非正则表达式方法。

【讨论】:

    猜你喜欢
    • 2012-01-24
    • 1970-01-01
    • 1970-01-01
    • 2019-07-27
    • 1970-01-01
    • 2016-03-22
    • 2014-10-29
    • 2017-06-17
    • 1970-01-01
    相关资源
    最近更新 更多