匹配非打印/非ASCII字符并从文本中删除

37

我的JavaScript比较生疏,所以希望能得到帮助。我需要在字符串中检测非打印字符(控制字符,如SOH、BS等)和扩展ASCII字符,例如Ž,并将它们删除,但我不确定如何编写代码?

有没有人可以指点我该怎么做?这是我目前的代码:

$(document).ready(function() {
    $('.jsTextArea').blur(function() {
        var pattern = /[^\000-\031]+/gi;
        var val = $(this).val();
        if (pattern.test(val)) {    
        for (var i = 0; i < val.length; i++) {
            var res = val.charAt([i]);
                alert("Character " + [i] + " " + res);              
        }          
    }
    else {
         alert("It failed");
     }

    });
});

match 属性应该这样调用:isNonAscii.match($(this).val())。程序并不会自动知道您想要将输入的值与正则表达式匹配。 - SeinopSys
感谢您的输入。很有道理,但我该如何从文本框中的字符串中删除检测到的无效字符? - Grant Doole
1
我已决定改变我的方法,并采用服务器端解决方案(因为客户端浏览器中有时可能会关闭JavaScript)。 - Grant Doole
1
@GrantDoole:请勿通过完全更改问题的代码来使现有答案失效。 - Cerbrus
5个回答

93

要匹配不属于可打印基本ASCII范围内的字符,您可以使用这个简单的正则表达式:

[^ -~]+

解释:在ASCII表的前128个字符中,可打印字符范围从空格字符开始,以波浪符结束。这些是您要保留的字符。该范围用[ -~]表示,而不在该范围内的字符用[^ -~]表示。这些需要替换。因此:

result = string.replace(/[^ -~]+/g, "");

嗨,非常好的回答,我已经接近解决了。虽然 value.replace 很有效,但不完全符合我的需求。我会更新原帖,展示我目前的进展。 - Grant Doole
1
嗨。FYI:这对于像“şıç”(土耳其语)这样的特殊字符无效。将替换它们并破坏单词。 - Canser Yanbakan
是的,对于韩文字符也是一样的。 - fraserh
这个正则表达式缺少删除符,它是最后一个ASCII字符,是不可打印的控制字符。 - Zamicol
@Zamicol 你确定吗?DEL~之后,而这个正则表达式匹配的是从Space~范围之外的所有内容。 - Arthur Khazbs
显示剩余3条评论

41

无需测试,您可以直接处理文本框内容:

textBoxContent = textBoxContent.replace(/[^\x20-\x7E]+/g, '');

\x20-\x7E 表示 ASCII 表中可打印的字符范围。

以您的代码为例:

$('.jsTextArea').blur(function() {
    this.value = this.value.replace(/[^\x20-\x7E]+/g, '');
});

谢谢您的输入,但这不起作用,因为替换函数仅适用于可打印字符。控制字符(例如BS、SOH、ACK等)是不可见的,因此无法使用.replace方法捕获它们。 - Grant Doole
@GrantDoole:真是个疯狂的想法!仅仅因为一个字符不可打印并不意味着替换方法无法找到它!替换方法适用于任何字符(可打印或不可打印)。 - Casimir et Hippolyte
真的吗?那很奇怪,因为我刚刚测试了一下,它没有工作。你能给我看一下吗? - Grant Doole
@GrantDoole:我会在我的答案中添加一个小测试。 - Casimir et Hippolyte
@GrantDoole:我忘记加上g修饰符了,这可能就是为什么你没有得到预期的结果。 - Casimir et Hippolyte
显示剩余5条评论

5

对于那些寻求可以处理 ASCII 以外字符且不会剥离 Unicode 字符的解决方案的人:

function stripNonPrintableAndNormalize(text) {
    // strip control chars
    text = text.replace(/\p{C}/gu, '');

    // other common tasks are to normalize newlines and other whitespace

    // normalize newline
    text = text.replace(/\n\r/g, '\n');
    text = text.replace(/\p{Zl}/gu, '\n');
    text = text.replace(/\p{Zp}/gu, '\n');

    // normalize space
    text = text.replace(/\p{Zs}/gu, ' ');

    return text;
}

各种Unicode类别标识符(例如行分隔符的Zl)在https://www.unicode.org/reports/tr44/#GC_Values_Table中定义,并如下所示:
缩写 全称 描述
Lu 大写字母 一个大写字母
Ll 小写字母 一个小写字母
Lt 标题字母 一个由两个字符组成的字符,第一个字符是大写字母
LC 大小写字母 Lu | Ll | Lt
Lm 修饰符字母 一个修饰符字母
Lo 其他字母 其他字母,包括音节和表意文字
L 字母 Lu | Ll | Lt | Lm | Lo
Mn 非间距标记 一个非间距组合标记(零宽度)
Mc 间距标记 一个间距组合标记(正向宽度)
Me 封闭标记 一个封闭组合标记
M 标记 Mn | Mc | Me
Nd 十进制数字 一个十进制数字
Nl 字母数字 一个类字母的数字字符
No 其他数字 其他类型的数字字符
N 数字 Nd | Nl | No
Pc 连接标点符号 连接标点符号,如连字符
Pd 破折号标点符号 破折号或连字符标点符号
Ps 开始标点符号 一个开始标点符号(成对使用)
Pe 结束标点符号 一个结束标点符号(成对使用)
Pi 初始标点符号 一个开头引用标记
Pf 末尾标点符号 一个结束引用标记
Po 其他标点符号 其他类型的标点符号
P 标点符号 Pc | Pd | Ps | Pe | Pi | Pf | Po
Sm 数学符号 一个用于数学的符号
Sc 货币符号 一个货币符号
Sk 修饰符号 非字母修饰符号
So 其他符号 其他类型的符号
S 符号 Sm

1
你需要将一个模式(而不是字符串)分配到isNonAscii变量中,然后使用test()检查它是否匹配。 test()返回true或false。
$(document).ready(function() {
    $('.jsTextArea').blur(function() {
        var pattern = /[^\000-\031]+/gi;
        var val = $(this).val();
        if (pattern.test(val)) {
            alert("It matched");
        }
        else {
            alert("It did NOT match");
        }
    });
});

请检查 jsFiddle


非常感谢您的回复,但是我该如何检测无效字符,将其从字符串中删除,并将不含无效字符的新字符串替换回文本框中? - Grant Doole
使用 replace() 函数应该可以按预期工作。你可以直接这样做,而不使用那段代码。@CasimiretHippolyte 的代码正常运行。 - kosmos

-5

对于那些遇到这个问题并正在寻找“一劳永逸”的解决方案的人... 这就是我最终解决它的方法:

public static string RemoveTroublesomeCharacters(string inString)
{
    if (inString == null)
    {
        return null;
    }

    else
    {
        char ch;
        Regex regex = new Regex(@"[^\u0000-\u007F]", RegexOptions.IgnoreCase);
        Match charMatch = regex.Match(inString);

        for (int i = 0; i < inString.Length; i++)
        {
            ch = inString[i];
            if (char.IsControl(ch))
            {
                string matchedChar = ch.ToString();
                inString = inString.Replace(matchedChar, string.Empty);
            }
        }

        while (charMatch.Success)
        {
            string matchedChar = charMatch.ToString();
            inString = inString.Replace(matchedChar, string.Empty);
            charMatch = charMatch.NextMatch();
        }
    }       

    return inString;
}

对于那些经验较少的人,我将更详细地解释一下:

  1. 我们首先循环遍历整个字符串的每个字符,并使用char的IsControl方法来确定一个字符是否是控制字符。

  2. 如果找到控制字符,则将匹配的字符复制到一个字符串中,然后使用Replace方法将控制字符更改为空字符串。对于字符串的其余部分,重复此过程。

  3. 一旦我们遍历完整个字符串,我们就会使用定义的正则表达式(它将匹配任何不是控制字符或标准ASCII字符的字符),并再次用空字符串替换匹配的字符。在while循环中执行此操作意味着只要charMatch为true,字符就会被替换。

  4. 最后,一旦所有字符都被删除并且我们已经遍历了整个字符串,我们就返回inString。

(注意:我仍然没有弄清楚如何使用新修改的inString值重新填充TextBox,所以如果有人能指出如何做到这一点,那就太好了)


9
你在这里给出了完全有效的答案,你的解决方案基于它们。另外,\u0000-\u0020 是控制字符。 - Zlatin Zlatev
2
谁说他不应该概述他如何使用其他答案(其中一个被标记为已接受)最终解决了他的问题?有人可能会认为,SO的目的就是实现这样的结果。 - m12lrpv

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接