【问题标题】:How to remove invalid UTF-8 characters from a JavaScript string?如何从 JavaScript 字符串中删除无效的 UTF-8 字符?
【发布时间】:2011-02-09 19:59:57
【问题描述】:

我想从 JavaScript 中的字符串中删除所有无效的 UTF-8 字符。我用这个 JavaScript 试过了:

strTest = strTest.replace(/([\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})|./g, "$1");

似乎这里描述的 UTF-8 验证正则表达式 (链接已删除) 更完整,我以相同的方式对其进行了调整,例如:

strTest = strTest.replace(/([\x09\x0A\x0D\x20-\x7E]|[\xC2-\xDF][\x80-\xBF]|\xE0[\xA0-\xBF][\x80-\xBF]|[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}|\xED[\x80-\x9F][\x80-\xBF]|\xF0[\x90-\xBF][\x80-\xBF]{2}|[\xF1-\xF3][\x80-\xBF]{3}|\xF4[\x80-\x8F][\x80-\xBF]{2})|./g, "$1");

这两段代码似乎都允许有效的 UTF-8 通过,但几乎没有从我的测试数据中过滤掉任何错误的 UTF-8 字符:UTF-8 decoder capability and stress test。坏字符要么原封不动地通过,要么似乎删除了一些字节,从而创建了一个新的无效字符。

我不太熟悉 UTF-8 标准或 JavaScript 中的多字节,所以我不确定我是否未能在正则表达式中表示正确的 UTF-8,或者我是否在JavaScript。

编辑:根据 Tomalak 的评论在我的正则表达式中添加了全局标志 - 但这仍然对我不起作用。根据 bobince 的评论,我将放弃在客户端执行此操作。

【问题讨论】:

标签: javascript regex utf-8


【解决方案1】:

西班牙语和法语等语言有“é”等重音字符,代码在 160-255 范围内,请参阅 https://www.ascii.cl/htmlcodes.htm

function cleanString(input) {
    var output = "";
    for (var i=0; i<input.length; i++) {
        if (input.charCodeAt(i) <= 127 || input.charCodeAt(i) >= 160 && input.charCodeAt(i) <= 255) {
            output += input.charAt(i);
        }
    }
    return output;
}

【讨论】:

    【解决方案2】:

    我使用@Ali 的解决方案不仅清理了我的字符串,还用 html 替换替换了无效字符:

     cleanString(input) {
        var output = "";
        for (var i = 0; i < input.length; i++) {
          if (input.charCodeAt(i) <= 127) {
            output += input.charAt(i);
          } else {
            output += "&#" + input.charCodeAt(i) + ";";
          }
        }
        return output;
      }
    

    【讨论】:

      【解决方案3】:

      我已经整理了上面提出的一些解决方案以保证错误安全

             var removeNonUtf8 = (characters) => {
                  try {
                      // ignore invalid char ranges
                      var bytelike = unescape(encodeURIComponent(characters));
                      characters = decodeURIComponent(escape(bytelike));
                  } catch (error) { }
                  // remove �
                  characters = characters.replace(/\uFFFD/g, '');
                  return characters;
              },
      

      【讨论】:

      • 'unescape' 和 'escape' 不应再使用,未来的浏览器可能不支持。
      • 谢谢,关于escapeunescape 替换的好答案在这里stackoverflow.com/a/51175973/758836
      • 我相信,“好答案”仅指邮件链接,而不是此处提出的任何问题。 StackOverflow 上提供了替代函数,但没有一个经过测试,而且我在 Web 上还找不到任何经过测试的函数。 Unicode 真的很难操作。
      【解决方案4】:

      如果您尝试从 javascript 字符串中删除“无效字符” - � - 那么您可以像这样删除它们:

      myString = myString.replace(/\uFFFD/g, '')
      

      【讨论】:

        【解决方案5】:

        我使用这种简单而可靠的方法:

        function cleanString(input) {
            var output = "";
            for (var i=0; i<input.length; i++) {
                if (input.charCodeAt(i) <= 127) {
                    output += input.charAt(i);
                }
            }
            return output;
        }
        

        基本上,您真正想要的只是 ASCII 字符 0-127,因此只需逐个字符地重建字符串。如果它是一个好的字符,请保留它 - 如果不是,则丢弃它。相当强大,如果卫生是您的目标,它就足够快了(事实上它真的很快)。

        【讨论】:

        • 输出 += input.charCodeAt(i)
        • 带 ramda 的单线:const cleanString = input =&gt; R.map(char =&gt; char.charCodeAt(0) &lt;= 127 ? char : '', input).join('');
        • 单行无 ramda:const cleanString = input =&gt; Array.of(input).map(char =&gt; char.charCodeAt(0) &lt;= 127 ? char : '', input).join('')
        • 我不相信 docodemore 的版本有效,Array.of(input) 返回一个单元素数组。我想你想要这个:const cleanString = input =&gt; input.split('').map(char =&gt; char.charCodeAt(0) &lt;= 127 ? char : '').join('')
        • 查看stackoverflow.com/a/57593674/1955957 了解法语、西班牙语和其他“拉丁”语言
        【解决方案6】:

        我从数字图像的拍摄日期数据中遇到了一个非常奇怪的结果。无可否认,我的场景是独一无二的 - 使用 Windows 脚本主机 (wsh) 和 Shell.Application activex 对象,该对象允许获取文件夹的命名空间对象并调用 GetDetailsOf 函数以在操作系统解析后基本上返回 exif 数据。

        var app = new ActiveXObject("Shell.Application");
        var info = app.Namespace("c:\");
        var date = info.GetDetailsOf(info.ParseName("testimg.jpg"), 12);
        

        在windws vista和7中,结果如下:

        ?8/?27/?2011 ??11:45 PM

        所以我的做法如下:

        var chars = date.split(''); //split into characters
        var clean = "";
        for (var i = 0; i < chars.length; i++) {
           if (chars[i].charCodeAt(0) < 255) clean += chars[i];
        }
        

        结果当然是排除了那些问号字符的字符串。

        我知道您完全采用了不同的解决方案,但我想我会发布我的解决方案,以防其他人遇到此问题并且无法使用服务器端语言方法。

        【讨论】:

          【解决方案7】:

          简单的错误,大的影响:

          strTest = strTest.replace(/your regex here/g, "$1");
          // ----------------------------------------^
          

          没有“全局”标志,替换仅发生在第一个匹配项。

          旁注:要删除任何不满足某种复杂条件的字符,例如落入一组特定 Unicode 字符范围,您可以使用否定前瞻:

          var re = /(?![\x00-\x7F]|[\xC0-\xDF][\x80-\xBF]|[\xE0-\xEF][\x80-\xBF]{2}|[\xF0-\xF7][\x80-\xBF]{3})./g;
          strTest = strTest.replace(re, "")
          

          re 读作如下

          (?! # 负前瞻:一个位置*后面没有*: […] # 上面任何允许的字符范围 ) # 结束前瞻 . # 匹配此字符(仅当满足先前条件时!)

          【讨论】:

          • 谢谢,这是我的代码中的一个大缺陷。不幸的是,现在有了全局标志,我发布的两个正则表达式似乎都在过滤任何不是 ASCII 的东西。 “压力测试”数据的第一个测试是一些有效的 UTF-8 文本被剥离,如果我从 columbia.edu/kermit/utf8.html 获取样本文本,除了 ASCII 之外的所有内容都会被删除。
          【解决方案8】:

          JavaScript 字符串本身就是 Unicode。它们包含字符序列*而不是字节序列,因此不可能包含无效的字节序列。

          (从技术上讲,它们实际上包含 UTF-16 代码单元序列,这并不完全相同,但这可能不是您现在需要担心的任何事情。)

          如果出于某种原因需要,您可以创建一个字符串来保存用作字节占位符的字符。 IE。使用字符U+0080 ('\x80') 代表字节0x80。如果您使用 UTF-8 将字符编码为字节,然后错误地使用 ISO-8859-1 将它们解码回字符,这就是您将得到的结果。有一个特殊的 JavaScript 习惯用法:

          var bytelike= unescape(encodeURIComponent(characters));
          

          并再次从 UTF-8 伪字节恢复为字符:

          var characters= decodeURIComponent(escape(bytelike));
          

          (值得注意的是,这几乎是唯一一次应该使用 escape/unescape 函数。它们在任何其他程序中的存在几乎总是一个错误。)

          decodeURIComponent(escape(bytes)),因为它的行为类似于 UTF-8 解码器,如果输入的代码单元序列不能作为 UTF-8 字节接受,则会引发错误。

          您很少需要在 JavaScript 中处理这样的字节字符串。最好在客户端继续以 Unicode 本地工作。浏览器将负责在线上对字符串进行 UTF-8 编码(在表单提交或 XMLHttpRequest 中)。

          【讨论】:

          • 感谢您提供信息丰富的答案——基本上我正在做的事情很困难,因为我不应该这样做。我在后端的某些字符上遇到问题,需要在那里解决。
          • 字符串"\uD800"无效,会导致encodeURIComponent抛出。
          • @OrangeDog:是的,因为该代码单元序列没有 UTF-8 表示。
          • 说 javascript 字符串不可能包含无效的字节序列是一个很好的理论,这也是我所期望的......但是,我目前正在尝试解决一个节点问题,即由包含无效 UTF8 字符的字符串(从 mongodb 返回)引起。因此,这显然是可能的 =]
          • @bobince 关于您的最后一行,浏览器不会转换使用 setRequestHeader 手动设置的标头值,并且在给定非 utf 值时会令人遗憾地崩溃。更好地期待它;)
          猜你喜欢
          • 2013-12-22
          • 2018-01-23
          • 1970-01-01
          • 2019-04-30
          • 2018-01-05
          • 1970-01-01
          • 2017-04-16
          • 1970-01-01
          • 2012-01-21
          相关资源
          最近更新 更多