Jak mogę zamienić niedrukowalne znaki Unicode w Javie?

88

Następujące znaki zastąpią znaki sterujące ASCII (skrót [\x00-\x1F\x7F]):

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

Poniższe zastąpi wszystkie niedrukowalne znaki ASCII (skrót [\p{Graph}\x20]), w tym znaki akcentowane:

my_string.replaceAll("[^\\p{Print}]", "?");

Jednak żaden z nich nie działa w przypadku ciągów Unicode. Czy ktoś ma dobry sposób na usunięcie niedrukowalnych znaków z ciągu znaków Unicode?

dagnelies
źródło
2
Jako dodatek: listę kategorii ogólnych Unicode można znaleźć w UAX # 44
McDowell
1
@Stewart: cześć, czy spojrzałeś na pytanie / odpowiedzi poza tytułem?!?
dagnelies
1
@Stewart: to inne pytanie dotyczy tylko podzbioru znaków ASCII niedrukowalnych !!!
dagnelies

Odpowiedzi:

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

Zobacz więcej o wyrażeniach regularnych Unicode . java.util.regexPattern/ String.replaceAllwspiera ich.

Op De Cirkel
źródło
Przynajmniej w Javie 1.6 nie ma dla nich wsparcia. download.oracle.com/javase/6/docs/api/java/util/regex/ ... ... Wypróbowałem też twoją linię, a poza brakiem ukośnika odwrotnego po prostu nie działa.
dagnelies
To działa: char c = 0xFFFA; String.valueOf(c).replaceAll("\\p{C}", "?");również w javadoc do wyszukiwania wzorców w sekcji obsługi Unicode , mówi, że obsługuje kategorie
Op De Cirkel
Masz rację! Przepraszam. Nie zauważyłem tego, ponieważ musiałem dodać kategorie Zl Zp, ponieważ były one głównie źródłem problemów. Działa doskonale. Czy mógłbyś dokonać mini edycji swojego posta, abym mógł ponownie zagłosować?
dagnelies
6
Istnieją również niewidoczne białe znaki (takie jak 0x0200B), które są częścią grupy \ p {Zs}. Niestety, ten zawiera również zwykłe białe znaki. Dla tych, którzy próbują odfiltrować ciąg wejściowy, który nie powinien zawierać żadnych spacji, łańcuch s.replaceAll("[\\p{C}\\p{Z}]", "")spełni urok
Andrey L
1
To jest to, czego szukałem, próbowałem, replaceAll("[^\\u0000-\\uFFFF]", "")ale nie udało mi się
Bibaswann Bandyopadhyay
58

Op De Cirkel ma w większości rację. Jego sugestia zadziała w większości przypadków:

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

Ale jeśli myStringmoże zawierać punkty kodowe inne niż BMP, jest to bardziej skomplikowane. \p{C}zawiera zastępcze punkty kodowe \p{Cs}. Powyższa metoda zamiany spowoduje uszkodzenie punktów kodowych innych niż BMP, czasami zastępując tylko połowę pary zastępczej. Możliwe, że jest to błąd języka Java, a nie zamierzone zachowanie.

Istnieje możliwość skorzystania z innych kategorii składników:

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

Jednak pojedyncze znaki zastępcze niebędące częścią pary (każdy znak zastępczy ma przypisany punkt kodowy) nie zostaną usunięte. Podejście inne niż wyrażenia regularne to jedyny sposób, jaki znam, aby poprawnie obsługiwać \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;
    }
}
noackjr
źródło
8

Możesz być zainteresowany kategoriami Unicode „Inne, sterowanie” i prawdopodobnie „Inny, format” (niestety ten drugi wydaje się zawierać zarówno znaki niedrukowalne, jak i drukowalne).

W wyrażeniach regularnych Java możesz je sprawdzić używając odpowiednio \p{Cc}i \p{Cf}.

Joachim Sauer
źródło
Cóż, szkoda, że ​​wyrażenia java ich nie mają, ale przynajmniej mam teraz listę ... lepsze niż nic. dzięki
dagnelies
4

metody dla Twojego celu

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]", "");
} 
Ali Bagheri
źródło
0

Użyłem do tego prostej funkcji:

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

Mam nadzieję, że to jest przydatne.

user1300830
źródło
0

Opierając się na odpowiedziach Op De Cirkel i noackjr , wykonuję następujące czynności w celu ogólnego czyszczenia ciągów: 1. przycinanie wiodących lub końcowych białych znaków, 2. dos2unix, 3. mac2unix, 4. usuwanie wszystkich „niewidocznych znaków Unicode” z wyjątkiem białych znaków:

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

Przetestowano za pomocą Scala REPL.

RyanLeiTaiwan
źródło
0

Proponuję usunąć niedrukowalne znaki, jak poniżej, zamiast je zastąpić

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();
}
Ramesh Bathini
źródło
-4

Przeprojektowałem kod dla numerów telefonów +9 (987) 124124 Wyodrębniłem cyfry z ciągu znaków w Javie

 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 );
}
Kairat Koibagarov
źródło