如何从JavaScript字符串中删除无效的UTF-8字符?

时间:2010-04-19 19:03:12

标签: javascript regex utf-8

我想从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的评论,我放弃了在客户端这样做。

8 个答案:

答案 0 :(得分:23)

我使用这种简单而坚固的方法:

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所以只需用char重建字符串char。如果这是一个很好的焦炭,保留它 - 如果没有,抛弃它。相当健壮,如果卫生是你的目标,它足够快(事实上它真的很快)。

答案 1 :(得分:20)

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

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

如果由于某种原因需要,可以创建一个字符串,其中包含用作字节占位符的字符。即。使用字符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中)。

答案 2 :(得分:8)

简单的错误,大的影响:

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

没有“global”标志,仅对第一场比赛进行替换。

附注:要删除不符合某些复杂条件的任何字符,例如落入某组某些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读为

(?!      # negative look-ahead: a position *not followed by*:
  […]    #   any allowed character range from above
)        # end lookahead
.        # match this character (only if previous condition is met!)

答案 3 :(得分:8)

如果您尝试删除&#34;无效字符&#34; - � - 从javascript字符串然后你可以像这样摆脱它们:

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

答案 4 :(得分:2)

我遇到了这个问题,结果来自数字图像的Date Taken数据。我的场景无疑是独一无二的 - 使用Windows脚本主机(wsh)和Shell.Application activex对象,该对象允许获取文件夹的命名空间对象,并且在操作系统解析后,调用GetDetailsOf函数实质上返回exif数据。 / p>

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

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

我知道你完全采用了不同的解决方案,但我认为我会发布我的解决方案以防其他人遇到麻烦,并且不能使用服务器端语言方法。

答案 5 :(得分: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;
}

答案 6 :(得分:0)

我将上面提出的一些解决方案放在一起以确保错误安全性

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

答案 7 :(得分:0)

我使用@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;
  }