如何在Java中替换不可打印的Unicode字符?

99

以下将替换ASCII控制字符(简写为[\x00-\x1F\x7F]):

my_string.replaceAll("\\p{Cntrl}", "?");
下面的代码将替换所有ASCII不可打印字符(缩写为[\p{Graph}\x20]),包括带重音符号的字符:
my_string.replaceAll("[^\\p{Print}]", "?");

然而,这两种方法都无法处理Unicode字符串。有没有好的方法可以从Unicode字符串中删除非可打印字符?


2
仅作为补充说明:Unicode通用类别列表可以在UAX#44中找到。 - McDowell
1
@Stewart:嗨,你看过标题以外的问题/答案了吗?!? - dagnelies
1
@Stewart:那个问题只涵盖了非打印字符的ASCII子集!!! - dagnelies
9个回答

168
my_string.replaceAll("\\p{C}", "?");

了解更多关于Unicode正则表达式的信息。 java.util.regex.Pattern/String.replaceAll支持它们。


在 Java 1.6 中,至少目前还不支持它们。http://download.oracle.com/javase/6/docs/api/java/util/regex/Pattern.html...我也尝试了你的代码行,除了缺少一个反斜杠之外,它根本就不起作用。 - dagnelies
9
还有一些看不见的空格字符(比如0x0200B),它们属于\p{Zs}字符组。不幸的是,这个字符组也包括普通空格。对于那些试图过滤不应包含任何空格的输入字符串的人来说,使用字符串s.replaceAll("[\\p{C}\\p{Z}]", "")即可达到目的。 - Andrey L
2
这就是我正在寻找的内容,我尝试了 replaceAll("[^\\u0000-\\uFFFF]", "") 但没有成功。 - Bibaswann Bandyopadhyay
谢谢您的回答。我们正在使用这个过滤器,但是表情符号被剥离了。如何在不影响表情符号的情况下去除非可打印字符? - quzhi65222714
3
注意:这个解决方案(获得了150个赞)也会删除换行符,而您可能不希望它们被替换。 - basZero
显示剩余3条评论

71

Op De Cirkel大部分是正确的。他的建议在大多数情况下都有效:

myString.replaceAll("\\p{C}", "?");

但是,如果myString可能包含非BMP码点,则情况会更加复杂。\p{C}包含\p{Cs}的代理码位。上述替换方法有时仅替换代理对的一半,从而破坏非BMP码点。这可能是Java中的一个漏洞,而不是意图行为。

使用其他组成类别是一种选择:

myString.replaceAll("[\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}]", "?");

然而,单个代理字符不属于一对(每个代理字符都有一个指定的代码点)将不会被删除。我知道正确处理\p{C}的唯一方法是采用非正则表达式方法:

StringBuilder newString = new StringBuilder(myString.length());
for (int offset = 0; offset < myString.length();)
{
    int codePoint = myString.codePointAt(offset);
    offset += Character.charCount(codePoint);

    // Replace invisible control characters and unused code points
    switch (Character.getType(codePoint))
    {
        case Character.CONTROL:     // \p{Cc}
        case Character.FORMAT:      // \p{Cf}
        case Character.PRIVATE_USE: // \p{Co}
        case Character.SURROGATE:   // \p{Cs}
        case Character.UNASSIGNED:  // \p{Cn}
            newString.append('?');
            break;
        default:
            newString.append(Character.toChars(codePoint));
            break;
    }
}

11
下面是实现您目标的方法。
public static String removeNonAscii(String str)
{
    return str.replaceAll("[^\\x00-\\x7F]", "");
}

public static String removeNonPrintable(String str) // All Control Char
{
    return str.replaceAll("[\\p{C}]", "");
}

public static String removeSomeControlChar(String str) // Some Control Char
{
    return str.replaceAll("[\\p{Cntrl}\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}]", "");
}

public static String removeFullControlChar(String str)
{
    return removeNonPrintable(str).replaceAll("[\\r\\n\\t]", "");
} 

8

很遗憾,Java表达式没有它们,但至少我现在有了列表...总比没有好。谢谢。 - dagnelies

0
我已经使用了这个简单的函数:
private static Pattern pattern = Pattern.compile("[^ -~]");
private static String cleanTheText(String text) {
    Matcher matcher = pattern.matcher(text);
    if ( matcher.find() ) {
        text = text.replace(matcher.group(0), "");
    }
    return text;
}

希望这对你有用。

那个正则表达式是做什么的? - ziggy

0

根据Op De Cirkelnoackjr的回答,以下是我用于通用字符串清理的步骤:1. 去除前导或尾随空格,2. dos2unix,3. mac2unix,4. 移除所有“不可见的Unicode字符”,除了空格:

myString.trim.replaceAll("\r\n", "\n").replaceAll("\r", "\n").replaceAll("[\\p{Cc}\\p{Cf}\\p{Co}\\p{Cn}&&[^\\s]]", "")

已在Scala REPL中进行测试。


0

我建议删除非可打印字符,而不是替换它们。

private String removeNonBMPCharacters(final String input) {
    StringBuilder strBuilder = new StringBuilder();
    input.codePoints().forEach((i) -> {
        if (Character.isSupplementaryCodePoint(i)) {
            strBuilder.append("?");
        } else {
            strBuilder.append(Character.toChars(i));
        }
    });
    return strBuilder.toString();
}

0

支持多语言

public static String cleanUnprintableChars(String text, boolean multilanguage)
{
    String regex = multilanguage ? "[^\\x00-\\xFF]" : "[^\\x00-\\x7F]";
    // strips off all non-ASCII characters
    text = text.replaceAll(regex, "");

    // erases all the ASCII control characters
    text = text.replaceAll("[\\p{Cntrl}&&[^\r\n\t]]", "");

    // removes non-printable characters from Unicode
    text = text.replaceAll("\\p{C}", "");

    return text.trim();
}

-4

我已经重新设计了电话号码+9(987)124124的代码 在Java中从字符串中提取数字

 public static String stripNonDigitsV2( CharSequence input ) {
    if (input == null)
        return null;
    if ( input.length() == 0 )
        return "";

    char[] result = new char[input.length()];
    int cursor = 0;
    CharBuffer buffer = CharBuffer.wrap( input );
    int i=0;
    while ( i< buffer.length()  ) { //buffer.hasRemaining()
        char chr = buffer.get(i);
        if (chr=='u'){
            i=i+5;
            chr=buffer.get(i);
        }

        if ( chr > 39 && chr < 58 )
            result[cursor++] = chr;
        i=i+1;
    }

    return new String( result, 0, cursor );
}

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