Jak sprawdzić, czy ciąg zawiera tylko ASCII?

120

Wywołanie Character.isLetter(c)powraca, truejeśli znak jest literą. Ale czy istnieje sposób, aby szybko sprawdzić, czy a Stringzawiera tylko podstawowe znaki ASCII?

TambourineMan
źródło

Odpowiedzi:

128

Począwszy od Guava 19.0 możesz używać:

boolean isAscii = CharMatcher.ascii().matchesAllOf(someString);

Używa matchesAllOf(someString)metody, która opiera się na metodzie fabrycznej, ascii()a nie na obecnie przestarzałym ASCIIsingletonie.

Tutaj ASCII zawiera wszystkie znaki ASCII tym niedrukowalne znaki mniejsze niż 0x20(spacja), takie jak tabulatory, przesunięcie wiersza / powrót, ale także BELkod 0x07i DELkod 0x7F.

Ten kod niepoprawnie używa znaków zamiast punktów kodowych, nawet jeśli punkty kodowe są wskazane w komentarzach do wcześniejszych wersji. Na szczęście znaki wymagane do utworzenia punktu kodowego o wartości równej U+010000lub większej używają dwóch zastępczych znaków o wartości spoza zakresu ASCII. Tak więc metoda nadal sprawdza się w testowaniu pod kątem ASCII, nawet dla ciągów zawierających emoji.

W przypadku wcześniejszych wersji Guava bez ascii()metody możesz napisać:

boolean isAscii = CharMatcher.ASCII.matchesAllOf(someString);
ColinD
źródło
31
+1 Chociaż dobrze jest, jeśli nie potrzebujesz innej biblioteki innej firmy, odpowiedź Colina jest znacznie krótsza i znacznie bardziej czytelna. Sugerowanie bibliotek zewnętrznych jest całkowicie w porządku i nie powinno być karane głosem negatywnym.
Jesper
1
Powinienem również zwrócić uwagę, że CharMatchers są naprawdę niesamowicie potężne i mogą zrobić więcej niż to. Ponadto istnieje wiele innych predefiniowanych CharMatcherów oprócz ASCII oraz świetne fabryczne metody tworzenia niestandardowych.
ColinD
7
CharMatcher.ASCIIjest obecnie przestarzały i wkrótce zostanie usunięty w czerwcu 2018 r.
thisarattr
108

Możesz to zrobić za pomocą java.nio.charset.Charset .

import java.nio.charset.Charset;

public class StringUtils {

  public static boolean isPureAscii(String v) {
    return Charset.forName("US-ASCII").newEncoder().canEncode(v);
    // or "ISO-8859-1" for ISO Latin 1
    // or StandardCharsets.US_ASCII with JDK1.7+
  }

  public static void main (String args[])
    throws Exception {

     String test = "Réal";
     System.out.println(test + " isPureAscii() : " + StringUtils.isPureAscii(test));
     test = "Real";
     System.out.println(test + " isPureAscii() : " + StringUtils.isPureAscii(test));

     /*
      * output :
      *   Réal isPureAscii() : false
      *   Real isPureAscii() : true
      */
  }
}

Wykryj znak spoza ASCII w łańcuchu

RealHowTo
źródło
10
Nie sądzę, by dobrym pomysłem było ustawienie statycznego CharsetEncoder, ponieważ zgodnie z dokumentacją „Instancje tej klasy nie są bezpieczne do użytku przez wiele współbieżnych wątków”.
pm_labs
@paul_sns, masz rację CharsetEncoder nie jest bezpieczny dla wątków (ale Charset jest), więc nie jest dobrym pomysłem, aby był statyczny.
RealHowTo
11
W Javie 1.7 lub nowszym można użyć StandardCharsets.US_ASCIIzamiast Charset.forName("US-ASCII").
Julian Lettner
@RealHowTo Prawidłowe rozwiązania nie powinny polegać na komentarzach, zadbać o naprawienie tego problemu i może skorzystać z metody oneliner opartej na StandardCharsets? Mógłbym opublikować inną odpowiedź, ale wolę naprawić tę bardzo cenioną odpowiedź.
Maarten Bodewes
77

Oto inny sposób, który nie zależy od biblioteki, ale używa wyrażenia regularnego.

Możesz użyć tej pojedynczej linii:

text.matches("\\A\\p{ASCII}*\\z")

Cały przykładowy program:

public class Main {
    public static void main(String[] args) {
        char nonAscii = 0x00FF;
        String asciiText = "Hello";
        String nonAsciiText = "Buy: " + nonAscii;
        System.out.println(asciiText.matches("\\A\\p{ASCII}*\\z"));
        System.out.println(nonAsciiText.matches("\\A\\p{ASCII}*\\z"));
    }
}
Arne Deutsch
źródło
15
\\ A - Początek wprowadzania ... \\ p {ASCII} * - Dowolny znak ASCII w dowolnym momencie ... \\ z - Koniec wprowadzania
Arne Deutsch
@ArneDeutsch masz coś przeciwko, gdybym poprawić odpowiedź i zawierać odniesienia do \P{Print}i \P{Graph}+ opis? Dlaczego potrzebujesz \Ai \z?
Maarten Bodewes
Co to za wyrażenie regularne? Wiem, że $ to koniec łańcucha, ^ to początek, nigdy nie słyszałem o żadnym z \\ A \\ p \\ z, czy możesz dołączyć odniesienie do javadoc?
deathangel908
@ deathangel908 \ A jest początkiem wprowadzania. \ z to koniec danych wejściowych. ^ i $ zachowują się inaczej w trybie MULTILINE, a DOTALL zmienia zachowanie \ A i \ z. Zobacz stackoverflow.com/a/3652402/1003157
Raymond Naseef
58

Przejdź przez ciąg i upewnij się, że wszystkie znaki mają wartość mniejszą niż 128.

Ciągi Java są kodowane koncepcyjnie jako UTF-16. W UTF-16 zestaw znaków ASCII jest kodowany jako wartości 0–127, a kodowanie dowolnego znaku innego niż ASCII (który może składać się z więcej niż jednego znaku Java) gwarantuje, że nie będzie zawierał liczb 0–127

JeremyP
źródło
27
Dzięki Javie 1.8 możesz:str.chars().allMatch(c -> c < 128)
Julian Lettner
7
Jeśli chcesz wydrukować znaki, które możesz chcieć przetestować, c >= 0x20 && c < 0x7Fponieważ pierwsze 32 wartości 7-bitowego kodowania to znaki kontrolne, a końcowa wartość (0x7F) to DEL.
Maarten Bodewes
15

Lub skopiuj kod z klasy IDN .

// to check if a string only contains US-ASCII code point
//
private static boolean isAllASCII(String input) {
    boolean isASCII = true;
    for (int i = 0; i < input.length(); i++) {
        int c = input.charAt(i);
        if (c > 0x7F) {
            isASCII = false;
            break;
        }
    }
    return isASCII;
}
Zaratustra
źródło
1
Działa to nawet z 2-znakowym unikodem, ponieważ pierwszy znak to> = U + D800
k3b
Ale pamiętaj, że zawiera niedrukowalne znaki w ASCII (co jest poprawne, ale można się tego nie spodziewać). Oczywiście możliwe jest bezpośrednie użycie return falsezamiast isASCII = falsei break.
Maarten Bodewes
To jest kod z Oracle JDK. Kopiowanie może powodować problemy prawne.
Arne Deutsch
11

commons-lang3 z Apache zawiera cenne narzędzia / metody ułatwiające rozwiązywanie wszelkiego rodzaju „problemów”, w tym tego.

System.out.println(StringUtils.isAsciiPrintable("!@£$%^&!@£$%^"));
fjkjava
źródło
1
Należy pamiętać, że isAsciiPrintable zwraca wartość false, jeśli ciąg zawiera znaki tabulacji lub znaku nowego wiersza (\ t \ r \ n).
TampaHaze
@ TampaHaze to dlatego, że wewnętrznie sprawdza, czy każda wartość znaku mieści się w zakresie od 32 do 127. Myślę, że to źle. Powinniśmy sprawdzić od 0 do 127
nadchodzi dzień
1
@therealprashant, jeśli nazwa metody to isAscii, zgadzam się z tobą. Jednak nazwana metoda toAsciiPrintable oznacza, że ​​mogły one celowo wykluczać znaki od 0 do 31.
TampaHaze
4

Spróbuj tego:

for (char c: string.toCharArray()){
  if (((int)c)>127){
    return false;
  } 
}
return true;
pforyogurt
źródło
„Spróbuj tego” zawsze otrzymuje negatywną opinię. Co to robi ? Co obejmuje, a co nie? Nawiasem mówiąc, dostałbym głos przeciw, ponieważ podwoiłeś rozmiar pamięci.
Maarten Bodewes
1

Przejdź przez ciąg i użyj funkcji charAt (), aby uzyskać znak char. Następnie potraktuj go jako int i zobacz, czy ma wartość Unicode (nadzbiór ASCII), którą lubisz.

Złam się w pierwszej kolejności, której nie lubisz.

Thorbjørn Ravn Andersen
źródło
1
private static boolean isASCII(String s) 
{
    for (int i = 0; i < s.length(); i++) 
        if (s.charAt(i) > 127) 
            return false;
    return true;
}
fdsfdsfdsfds
źródło
Tylko kod odpowiedz, proszę wskazać, co to robi, tj. Że zawiera znaki niedrukowalne i nieokreślony znak (0x7F), jeśli wykonujesz tę kontrolę.
Maarten Bodewes
Ten mógł mnie ugryźć po tym, jak mój długo działający program nie znalazł żadnych interesujących postaci. charAtzwraca a char. Czy możesz bezpośrednio sprawdzić, czy typchar jest większy niż int bez wcześniejszej konwersji na int, czy też twój test automatycznie wykonuje konwersję? Może możesz, a może tak? I poszedł do przodu i konwertowane do int to tak: if ((int)s.charAt(i) > 127). Nie jestem pewien, czy moje wyniki są inne, ale czuję się lepiej, pozwalając temu działać. Zobaczymy: - \
harperville
0

To było możliwe. Niezły problem.

import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

public class EncodingTest {

    static CharsetEncoder asciiEncoder = Charset.forName("US-ASCII")
            .newEncoder();

    public static void main(String[] args) {

        String testStr = "¤EÀsÆW°ê»Ú®i¶T¤¤¤ß3¼Ó®i¶TÆU2~~KITEC 3/F Rotunda 2";
        String[] strArr = testStr.split("~~", 2);
        int count = 0;
        boolean encodeFlag = false;

        do {
            encodeFlag = asciiEncoderTest(strArr[count]);
            System.out.println(encodeFlag);
            count++;
        } while (count < strArr.length);
    }

    public static boolean asciiEncoderTest(String test) {
        boolean encodeFlag = false;
        try {
            encodeFlag = asciiEncoder.canEncode(new String(test
                    .getBytes("ISO8859_1"), "BIG5"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return encodeFlag;
    }
}
user3614583
źródło
0

Spowoduje to zwrócenie wartości true, jeśli String zawiera tylko znaki ASCII i false, gdy nie

Charset.forName("US-ASCII").newEncoder().canEncode(str)

Jeśli chcesz usunąć kod spoza ASCII, oto fragment kodu:

if(!Charset.forName("US-ASCII").newEncoder().canEncode(str)) {
                        str = str.replaceAll("[^\\p{ASCII}]", "");
                    }
mike oganyan
źródło
-2
//return is uppercase or lowercase
public boolean isASCIILetter(char c) {
  return (c > 64 && c < 91) || (c > 96 && c < 123);
}
Lukas Greblikas
źródło
Kod odpowiada tylko za pomocą 4 magii i nie ma wyjaśnienia, co robi . Proszę dostosować.
Maarten Bodewes