在JavaScript中将十六进制字符串稳健地转换为字节值

时间:2016-09-08 14:37:44

标签: javascript hex byte converter

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

给定输入字符串"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;
}

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

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

2 个答案:

答案 0 :(得分:2)

更新的答案(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))。

答案 1 :(得分:0)

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;
}

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