Como verificar se uma String contém apenas ASCII?

120

A chamada Character.isLetter(c)retorna truese o caractere for uma letra. Mas existe uma maneira de descobrir rapidamente se a Stringcontém apenas os caracteres básicos do ASCII?

TambourineMan
fonte

Respostas:

128

De Guava 19.0 em diante, você pode usar:

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

Este usa o matchesAllOf(someString)método que depende do método de fábrica ascii()em vez do agora obsoletoASCII singleton .

Aqui, ASCII inclui todos os caracteres ASCII, incluindo os caracteres não imprimíveis abaixo de 0x20(espaço), como tabulações, alimentação de linha / retorno, mas também BELcom código 0x07e DELcom código 0x7F.

Esse código usa caracteres incorretamente em vez de pontos de código, mesmo se os pontos de código forem indicados nos comentários de versões anteriores. Felizmente, os caracteres necessários para criar o ponto de código com um valor igual U+010000ou superior a usam dois caracteres substitutos com um valor fora do intervalo ASCII. Portanto, o método ainda consegue testar o ASCII, mesmo para strings contendo emoji.

Para versões anteriores do Guava sem o ascii()método, você pode escrever:

boolean isAscii = CharMatcher.ASCII.matchesAllOf(someString);
ColinD
fonte
31
+1 Embora seja bom se você não precisa de outra biblioteca de terceiros, a resposta de Colin é muito mais curta e muito mais legível. Sugerir bibliotecas de terceiros é perfeitamente normal e não deve ser punido com um voto negativo.
Jesper
1
Devo também apontar que CharMatchers são realmente incrivelmente poderosos e podem fazer muuuuito mais do que isso. Além disso, existem muitos outros CharMatchers predefinidos além do ASCII, e excelentes métodos de fábrica para criar personalizados.
ColinD
7
CharMatcher.ASCIIestá obsoleto agora e está prestes a ser removido em junho de 2018.
thisarattr
108

Você pode fazer isso com 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
      */
  }
}

Detecta caracteres não ASCII em uma string

RealHowTo
fonte
10
Não acho que seja uma boa ideia tornar o CharsetEncoder estático, pois de acordo com a documentação "Instâncias desta classe não são seguras para uso por vários threads simultâneos."
pm_labs
@paul_sns, você está certo CharsetEncoder não é thread-safe (mas Charset é), então não é uma boa ideia torná-lo estático.
RealHowTo
11
Com Java 1.7 ou superior, pode-se usar em StandardCharsets.US_ASCIIvez de Charset.forName("US-ASCII").
Julian Lettner,
@RealHowTo Soluções corretas não devem depender de comentários, cuidado para corrigir esse problema e talvez usar um método oneliner baseado em StandardCharsets? Eu poderia postar outra resposta, mas prefiro corrigir esta resposta muito apreciada.
Maarten Bodewes
77

Aqui está outra maneira, não dependendo de uma biblioteca, mas usando um regex.

Você pode usar esta única linha:

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

Programa de exemplo completo:

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
fonte
15
\\ A - Início da entrada ... \\ p {ASCII} * - Qualquer caractere ASCII a qualquer momento ... \\ z - Fim da entrada
Arne Deutsch
@ArneDeutsch Você se importa se eu melhorar a resposta e incluir referências \P{Print}e \P{Graph}+ uma descrição? Por que você precisa \Ae \z?
Maarten Bodewes
O que é esse regex? Eu sei que $ é o fim da string, ^ é o início, nunca ouvi falar de \\ A \\ p \\ z, poderia anexar a referência a javadoc?
deathangel908 01 de
@ deathangel908 \ A é o início da entrada. \ z é o fim da entrada. ^ e $ se comportam de maneira diferente no modo MULTILINE, e DOTALL altera o comportamento de \ A e \ z. Consulte stackoverflow.com/a/3652402/1003157
Raymond Naseef
58

Faça a iteração pela string e certifique-se de que todos os caracteres tenham um valor menor que 128.

Java Strings são conceitualmente codificados como UTF-16. Em UTF-16, o conjunto de caracteres ASCII é codificado como os valores 0 - 127 e a codificação para qualquer caractere não ASCII (que pode consistir em mais de um caractere Java) tem garantia de não incluir os números 0 - 127

JeremyP
fonte
27
Com o Java 1.8 você pode fazer:str.chars().allMatch(c -> c < 128)
Julian Lettner 01 de
7
Se quiser caracteres imprimíveis, você pode querer testar, c >= 0x20 && c < 0x7Fpois os primeiros 32 valores da codificação de 7 bits são caracteres de controle e o valor final (0x7F) é DEL.
Maarten Bodewes,
15

Ou você copia o código da classe 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
fonte
1
Isso funciona até mesmo com unicode de 2 caracteres porque o primeiro caractere é> = U + D800
k3b
Mas observe que inclui caracteres não imprimíveis em ASCII (o que é correto, mas pode não ser esperado). Obviamente, é possível usar diretamente em return falsevez de usar isASCII = falsee break.
Maarten Bodewes
Este é o código do Oracle JDK. A cópia pode causar problemas legais.
Arne Deutsch
11

commons-lang3 do Apache contém métodos de utilidade / conveniência valiosos para todos os tipos de 'problemas', incluindo este.

System.out.println(StringUtils.isAsciiPrintable("!@£$%^&!@£$%^"));
fjkjava
fonte
1
Esteja ciente de que isAsciiPrintable retorna falso se a string contiver caracteres de tabulação ou alimentação de linha (\ t \ r \ n).
TampaHaze
@TampaHaze isso porque, internamente, ele verifica o valor de cada caractere entre 32 e 127. Acho que isso está errado. Devemos verificar de 0 a 127
realprashant
1
@therealprashant se o nome do método fosse isAscii, eu concordaria com você. Mas o método sendo nomeado isAsciiPrintable implica que eles podem ter excluído propositalmente os caracteres de 0 a 31.
TampaHaze 01 de
4

tente isto:

for (char c: string.toCharArray()){
  if (((int)c)>127){
    return false;
  } 
}
return true;
pforiogurte
fonte
"Tente isso" sempre recebe um voto negativo. O que isso faz ? O que está incluído e o que não está? Receberia um downvote porque você dobra o tamanho da memória também, aliás.
Maarten Bodewes
1

Faça a iteração pela string e use charAt () para obter o char. Em seguida, trate-o como um int e veja se ele tem um valor unicode (um superconjunto de ASCII) de sua preferência.

Quebre no primeiro que você não gosta.

Thorbjørn Ravn Andersen
fonte
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
fonte
Resposta apenas código, indique o que isso faz, ou seja, que inclui caracteres não imprimíveis e um caractere indefinido (0x7F) se você executar esta verificação.
Maarten Bodewes
Este pode ter me mordido depois que meu programa de longa duração não conseguiu encontrar nenhum personagem de interesse. charAtretorna a char. Você pode testar diretamente se um tipo charé maior do que um int sem converter em um int, primeiro, ou seu teste faz a conversão automaticamente? Talvez você possa e talvez seja? Fui em frente e convertido este para um int assim: if ((int)s.charAt(i) > 127). Não tenho certeza se meus resultados são diferentes, mas me sinto melhor em deixá-lo funcionar. Veremos: - \
Harperville
0

Foi possível. Belo problema.

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
fonte
0

Retornará verdadeiro se String contiver apenas caracteres ASCII e falso quando não contiver

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

Se você deseja remover não ASCII, aqui está o snippet:

if(!Charset.forName("US-ASCII").newEncoder().canEncode(str)) {
                        str = str.replaceAll("[^\\p{ASCII}]", "");
                    }
Mike Oganyan
fonte
-2
//return is uppercase or lowercase
public boolean isASCIILetter(char c) {
  return (c > 64 && c < 91) || (c > 96 && c < 123);
}
Lukas Greblikas
fonte
Um código responde apenas com 4 magias, e nenhuma explicação do que faz . Por favor, ajuste.
Maarten Bodewes