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

Posted

技术标签:

【中文标题】如何替换 Java 中不可打印的 Unicode 字符?【英文标题】:How can I replace non-printable Unicode characters in Java? 【发布时间】:2011-09-06 03:20:36 【问题描述】:

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

my_string.replaceAll("\\pCntrl", "?");

以下内容将替换所有 ASCII 不可打印字符([\pGraph\x20] 的简写),包括重音字符:

my_string.replaceAll("[^\\pPrint]", "?");

但是,它们都不适用于 Unicode 字符串。有没有人有从 unicode 字符串中删除不可打印字符的好方法?

【问题讨论】:

作为附录:Unicode 通用类别列表可以在UAX #44中找到 Fastest way to strip all non-printable characters from a Java String的可能重复 @Stewart:嗨,除了标题,你看过问题/答案了吗?!? @Stewart:其他问题仅涵盖不可打印字符的 ascii 子集!!! 【参考方案1】:
my_string.replaceAll("\\pC", "?");

查看更多关于Unicode regex。 java.util.regexPattern/String.replaceAll 支持他们。

【讨论】:

至少在 java 1.6 中,不支持它们。 download.oracle.com/javase/6/docs/api/java/util/regex/… ...我也试过你的台词,除了缺少反斜杠之外,它显然根本不起作用。 这有效:char c = 0xFFFA; String.valueOf(c).replaceAll("\\pC", "?"); 也在 Unicode 支持 部分的模式查找的 javadoc 中,说它支持类别 还有一些不可见的空白字符(如 0x0200B),它们是 \pZs 组的一部分。不幸的是,这个也包括正常的空格。对于那些试图过滤不应该包含任何空格的输入字符串的人,字符串s.replaceAll("[\\pC\\pZ]", "") 将起到魅力 这就是我要找的,我正在尝试replaceAll("[^\\u0000-\\uFFFF]", ""),但没有成功 注意:这里介绍的这个解决方案(有 150 个赞成票)还删除了您可能不想被替换的换行符。【参考方案2】:

Op De Cirkel 大体上是对的。他的建议在大多数情况下都会奏效:

myString.replaceAll("\\pC", "?");

但如果myString 可能包含非 BMP 代码点,那么它会更复杂。 \pC 包含 \pCs 的代理代码点。上述替换方法有时会仅替换一半代理对,从而破坏非 BMP 代码点。这可能是 Java 错误而不是预期行为。

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

myString.replaceAll("[\\pCc\\pCf\\pCo\\pCn]", "?");

但是,不属于一对的单独代理字符(每个代理字符都有一个分配的代码点)将不会被删除。非正则表达式方法是我知道正确处理\pC 的唯一方法:

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:     // \pCc
        case Character.FORMAT:      // \pCf
        case Character.PRIVATE_USE: // \pCo
        case Character.SURROGATE:   // \pCs
        case Character.UNASSIGNED:  // \pCn
            newString.append('?');
            break;
        default:
            newString.append(Character.toChars(codePoint));
            break;
    

【讨论】:

【参考方案3】:

您可能对Unicode categories "Other, Control" 和可能 "Other, Format" 感兴趣(不幸的是后者似乎同时包含不可打印和可打印字符)。

在 Java 正则表达式中,您可以分别使用 \pCc\pCf 来检查它们。

【讨论】:

好吧,太糟糕了 java 表达式没有它们,但至少我现在得到了列表......总比没有好。谢谢【参考方案4】:

以下方法可实现您的目标

public static String removeNonAscii(String str)

    return str.replaceAll("[^\\x00-\\x7F]", "");


public static String removeNonPrintable(String str) // All Control Char

    return str.replaceAll("[\\pC]", "");


public static String removeSomeControlChar(String str) // Some Control Char

    return str.replaceAll("[\\pCntrl\\pCc\\pCf\\pCo\\pCn]", "");


public static String removeFullControlChar(String str)

    return removeNonPrintable(str).replaceAll("[\\r\\n\\t]", "");
 

【讨论】:

【参考方案5】:

我为此使用了这个简单的函数:

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;

希望这是有用的。

【讨论】:

【参考方案6】:

根据 Op De Cirkelnoackjr 的回答,以下是我对一般字符串清理所做的工作:1. 修剪前导或尾随空格,2. dos2unix , 3. mac2unix, 4. 删除除空格之外的所有“不可见的Unicode字符”:

myString.trim.replaceAll("\r\n", "\n").replaceAll("\r", "\n").replaceAll("[\\pCc\\pCf\\pCo\\pCn&amp;&amp;[^\\s]]", "")

使用 Scala REPL 测试。

【讨论】:

【参考方案7】:

我建议它删除下面的不可打印字符而不是替换它

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();

【讨论】:

【参考方案8】:

支持的多语言

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("[\\pCntrl&&[^\r\n\t]]", "");

    // removes non-printable characters from Unicode
    text = text.replaceAll("\\pC", "");

    return text.trim();

【讨论】:

【参考方案9】:

我重新设计了电话号码 +9 (987) 124124 的代码 Extract digits from a string in 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 );

【讨论】:

以上是关于如何替换 Java 中不可打印的 Unicode 字符?的主要内容,如果未能解决你的问题,请参考以下文章

如何在忽略不可编码字符的同时输出Python3(unicode)字符串

Vim 用 unicode 字符替换

中文字符替换为其unicode编码值小3的字符

在 Java 中打印 Unicode 或补充字符

用java如何把unicode码转成汉字?

如何将拉丁 unicode 字符替换为 [a-z] 字符