从字符串中删除非ASCII可打印字符

24

我得到了包括非ASCII字符和不可打印字符在内的用户输入,例如

\xc2d
\xa0
\xe7
\xc3\ufffdd
\xc3\ufffdd
\xc2\xa0
\xc3\xa7
\xa0\xa0

例如:

email : abc@gmail.com\xa0\xa0
street : 123 Main St.\xc2\xa0

期望的输出:

  email : abc@gmail.com
  street : 123 Main St.

使用Java删除它们的最佳方法是什么?
我尝试了以下方法,但似乎不起作用。

public static void main(String args[]) throws UnsupportedEncodingException {
        String s = "abc@gmail\\xe9.com";
        String email = "abc@gmail.com\\xa0\\xa0";

        System.out.println(s.replaceAll("\\P{Print}", ""));
        System.out.println(email.replaceAll("\\P{Print}", ""));
    }

输出

abc@gmail\xe9.com
abc@gmail.com\xa0\xa0

为什么你想要移除它们? - jtahlborn
1
@jtahlborn,Mongo 无法序列化这些值。 - daydreamer
1
@daydreamer [需要引用来源] \xc2d 是一个有效的Unicode字符。如果MongoDB使用UTF-8,它应该能够序列化它们。也许你在这里遇到了XY问题?你是如何序列化你的文本的? - Raedwald
7个回答

59

您的要求不够明确。在Java String中,所有字符都是Unicode字符。因此,如果您将它们删除,则会得到一个空字符串。我猜您的意思是想要删除任何非ASCII、非可打印字符。

String clean = str.replaceAll("\\P{Print}", "");

这里的\p{Print}表示可打印ASCII字符的POSIX字符类,而\P{Print}则是该类的补集。使用此表达式将所有不可打印的ASCII字符替换为空字符串。(额外的反斜杠是因为\在字符串文字中开始转义序列。)


显然,所有输入字符实际上都是ASCII字符,它们表示不可打印或非ASCII字符的可打印编码。Mongo不应该对这些字符串有任何问题,因为它们仅包含纯粹可打印的ASCII字符。

这听起来有点可疑。我认为发生的情况是数据确实包含不可打印和非ASCII字符,另一个组件(如日志框架)正在将其替换为可打印的表示形式。在您的简单测试中,您未能将可打印的表示形式翻译回原始字符串,因此错误地认为第一个正则表达式无效。

这只是我的猜测,但如果我误解了情况,你确实需要剥离文字字面意义的\xHH转义符号,那么你可以使用以下正则表达式。

String clean = str.replaceAll("\\\\x\\p{XDigit}{2}", "");

Java正则表达式库支持的所有语法都在Pattern类的API文档中有很好的列出。如果想更详细地了解这些语法的含义,我发现Regular-Expressions.info网站非常有用。


这个不起作用。可能是我做错了什么,但它不工作。 - daydreamer
1
@daydreamer,你能提供一个SSCCE来展示哪里出了问题吗? - erickson
@daydreamer 在Java源代码中,\\x没有任何特殊含义。在Stringchar字面量中的\\是一个转义序列,会被替换为\。如果你想要一个Unicode转义,请使用\uXXXX,其中XXXX是Unicode点的十六进制表示。 - erickson
啊,我明白了,但是我得到的输入就是我和你分享的那个,这是否意味着无法去掉它? - daydreamer
Java字符串中的所有字符都是Unicode字符,因此如果您将它们删除,就会得到一个空字符串。xD - paradocslover
显示剩余7条评论

16

使用Google GuavaCharMatcher,您可以移除任何不可打印字符,然后保留所有ASCII字符(删除任何重音符号),如下所示:

String printable = CharMatcher.INVISIBLE.removeFrom(input);
String clean = CharMatcher.ASCII.retainFrom(printable);

我不确定这是否是您真正想要的,但它会删除问题示例数据中表示为转义序列的任何内容。


5
注意,"INVISIBLE" 一词表示已被移除的空格,这可能有些奇怪,因为空格本身确实是“可打印”的。 - Andrew White

16

我知道现在可能有点晚了,但是以后参考:

String clean = str.replaceAll("\\P{Print}", "");

该函数用于删除所有不可打印字符,但这包括换行符\n(换行)、制表符\t和回车符\r,有时您可能想保留这些字符。

针对这个问题,请使用反向逻辑:

String clean = str.replaceAll("[^\\n\\r\\t\\p{Print}]", "");

因为在mongo-land中非常有用,可以防止shell输出大量编码的非ASCII字符(如果你想要事情变得简单,mongo真的非常偏爱utf-8),所以给它点赞。 - Mark Mullin
3
错误:非法转义字符。 将字符串中的非空格、回车、换行、制表符和可打印字符替换为空白字符:String clean = str.replaceAll("[^\\n\\r\\t\\p{Print}]", ""); \\ 正确的写法应该是 \P 而不是 \p - Well Smith
真的帮了我很多,谢谢 @Ivan - Prinkal Kumar

4
你可以尝试这段代码:
public String cleanInvalidCharacters(String in) {
    StringBuilder out = new StringBuilder();
    char current;
    if (in == null || ("".equals(in))) {
        return "";
    }
    for (int i = 0; i < in.length(); i++) {
        current = in.charAt(i);
        if ((current == 0x9)
                || (current == 0xA)
                || (current == 0xD)
                || ((current >= 0x20) && (current <= 0xD7FF))
                || ((current >= 0xE000) && (current <= 0xFFFD))
                || ((current >= 0x10000) && (current <= 0x10FFFF))) {
            out.append(current);
        }

    }
    return out.toString().replaceAll("\\s", " ");
}

我发现从 String 中删除无效字符的方法对我很有帮助。


3
这是很多的魔法数字。提取这些从句(尤其是范围)为恰当命名的本地变量怎么样? - Philipp Reichart

2
您可以使用java.text.normalizer。

0
输入 => "This \u7279text \u7279is what I need" 输出 => "This text is what I need"
如果您想从字符串中删除Unicode字符,就像上面的例子一样,这段代码可以工作。
Pattern unicodeCharsPattern = Pattern.compile("\\\\u(\\p{XDigit}{4})");
Matcher unicodeMatcher = unicodeChars.matcher(data);
String cleanData = null;
if (unicodeMatcher.find()) {
    cleanData = unicodeMatcher.replaceAll("");
}

0

这个简单的函数对我来说效果更好:

function remove_non_ascii(str) {
  
    if ((str===null) || (str===''))
         return false;
   else
     str = str.toString();
    
    return str.replace(/[^\x20-\x7E]/g, '');
}

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