美文网首页JavaScript
[JS] encodeURIComponent URIError

[JS] encodeURIComponent URIError

作者: 何幻 | 来源:发表于2020-09-10 13:43 被阅读0次

背景

今天在使用 siphash 计算字符串 hash 的时候,居然抛异常了。

const siphash = require('siphash');
const Long = require('long');

const cryptoStr = '0123456789ABCDEF';

const hash = input => {
  const key = siphash.string16_to_key(cryptoStr);
  const { l, h } = siphash.hash(key, input);

  const unsigned = true;
  const long = new Long(
    l,
    h,
    unsigned,
  );

  return long;
};

const main = () => {
  try {
    const r = hash('\udfff');
    debugger
  } catch (err) {
    debugger  // URIError: URI malformed
  }
};

main();

排查

分析原因,发现是 siphash#string_to_u8 函数中调用了 encodeURIComponent 抛异常了。

在网上查了一些资料后发现 encodeURIComponent 也是会抛异常的,

Note that a URIError will be thrown if one attempts to encode a surrogate which is not part of a high-low pair, ...

// high-low pair ok
console.log(encodeURIComponent('\uD800\uDFFF'));

// lone high surrogate throws "URIError: malformed URI sequence"
console.log(encodeURIComponent('\uD800'));

// lone low surrogate throws "URIError: malformed URI sequence"
console.log(encodeURIComponent('\uDFFF')); 

解决方案

(1)全转
在我的场景下,确实需要对 \udfff 计算 hash,而且我只需要拿到 '\udfff' 这一串字符就行了。
所以,我用以下函数对抛异常的字符串范围进行了转码,

const safelyEncode = input => input.replace(/[\ud800-\udfff]/g, match => `\\u${match.charCodeAt(0).toString(16)}`);
safelyEncode('\udfff')
> "\udfff"

(2)连续字符转换
如果考虑到只有独立的 high-low pair 字符才是有问题的,
可以使用以下方式进行转码,

// 正则无法从右向左匹配字符串
const reverse = input => input.split('').reverse().join('');
const transform = char => `\\u${char.charCodeAt(0).toString(16)}`;

// 匹配 high-low pair,或者单个 high-low 字符
const regExp = /([\ud800-\udfff][\ud800-\udfff])|([\ud800-\udfff](?![\ud800-\udfff]))/g;

const safelyEncode = input => reverse(
  reverse(input).replace(regExp, (match, captureHighLowPair, captureSingleHighLowChar, index, originInput) =>
    // 匹配一次 index 往后移动 n 个字符,不匹配就不回调原样输出
    captureSingleHighLowChar == null ? captureHighLowPair : reverse(transform(captureSingleHighLowChar))
  )
);
'\udfff\ud83c\udfc0'
> "�🏀"
safelyEncode('\udfff\ud83c\udfc0')
> "\udfff🏀"

参考

MDN: encodeURIComponent

相关文章

网友评论

    本文标题:[JS] encodeURIComponent URIError

    本文链接:https://www.haomeiwen.com/subject/pfhpektx.html