Как проверить, содержит ли строка только ASCII?

120

Вызов Character.isLetter(c)возвращается, trueесли символ является буквой. Но есть ли способ быстро определить, Stringсодержит ли a только базовые символы ASCII?

TambourineMan
источник

Ответы:

128

Начиная с Guava 19.0 и далее, вы можете использовать:

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

При этом используется matchesAllOf(someString)метод, основанный на фабричном методе, ascii()а не на устаревшем ASCIIсинглтоне.

Здесь ASCII включает все символы ASCII, включая непечатаемые символы ниже 0x20(пробела), такие как табуляция, перевод строки / возврат, но также BELс кодом 0x07и DELс кодом 0x7F.

В этом коде неправильно используются символы, а не кодовые точки, даже если кодовые точки указаны в комментариях к более ранним версиям. К счастью, символы, необходимые для создания кодовой точки со значением U+010000или больше, используют два суррогатных символа со значением вне диапазона ASCII. Таким образом, этот метод по-прежнему успешно тестирует ASCII даже для строк, содержащих эмодзи.

Для более ранних версий Guava без ascii()метода вы можете написать:

boolean isAscii = CharMatcher.ASCII.matchesAllOf(someString);
ColinD
источник
31
+1 Хотя это хорошо, если вам не нужна еще одна сторонняя библиотека, ответ Колина намного короче и намного читабельнее. Предложение сторонних библиотек совершенно нормально и не должно наказываться отрицательным голосом.
Джеспер
1
Я также должен отметить, что CharMatcher действительно невероятно мощны и могут сделать гораздо больше, чем это. Кроме того, есть еще много предопределенных CharMatcher, помимо ASCII, и отличные фабричные методы для создания собственных.
ColinD
7
CharMatcher.ASCIIустарел и будет
удален
108

Вы можете сделать это с помощью 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
      */
  }
}

Обнаружение не-ASCII-символа в строке

RealHowTo
источник
10
Я не думаю, что делать CharsetEncoder статическим, поскольку, согласно документам, «экземпляры этого класса небезопасны для использования несколькими параллельными потоками».
pm_labs
@paul_sns, вы правы, CharsetEncoder не является потокобезопасным (но Charset), поэтому делать его статическим - не лучшая идея.
RealHowTo
11
В Java 1.7 или выше можно использовать StandardCharsets.US_ASCIIвместо Charset.forName("US-ASCII").
Джулиан Леттнер
@RealHowTo Правильные решения не должны полагаться на комментарии, заботиться об устранении этой проблемы и, возможно, использовать метод одинарной прокладки на основе StandardCharsets? Я мог бы опубликовать другой ответ, но я бы предпочел исправить этот высоко оцененный ответ.
Maarten Bodewes
77

Вот еще один способ, не зависящий от библиотеки, а с использованием регулярного выражения.

Вы можете использовать эту единственную строку:

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

Полный пример программы:

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"));
    }
}
Арне Дойч
источник
15
\\ A - Начало ввода ... \\ p {ASCII} * - Любой символ ASCII в любое время ... \\ z - Конец ввода
Arne Deutsch
@ArneDeutsch Не возражаете, если я улучшу ответ и добавлю ссылки \P{Print}и \P{Graph}+ описание? Зачем тебе \Aи \z?
Maarten Bodewes
Что это за регулярное выражение? Я знаю, что $ - конец строки, ^ - начало, никогда не слышал ни о каком из \\ A \\ p \\ z, не могли бы вы приложить ссылку на javadoc?
deathangel908
@ deathangel908 \ A - начало ввода. \ z - конец ввода. ^ и $ ведут себя по-разному в режиме MULTILINE, а DOTALL изменяет поведение \ A и \ z. См stackoverflow.com/a/3652402/1003157
Raymond Naseef
58

Выполните итерации по строке и убедитесь, что все символы имеют значение меньше 128.

Строки Java концептуально кодируются как UTF-16. В UTF-16 набор символов ASCII кодируется как значения от 0 до 127, и кодировка любого символа, отличного от ASCII (который может состоять из более чем одного символа Java), гарантированно не включает числа от 0 до 127.

JeremyP
источник
27
С помощью Java 1.8 вы можете:str.chars().allMatch(c -> c < 128)
Джулиан Леттнер
7
Если вам нужны печатные символы, вы можете захотеть протестировать, c >= 0x20 && c < 0x7Fпоскольку первые 32 значения 7-битной кодировки являются управляющими символами, а конечное значение (0x7F) - DEL.
Маартен Бодевес,
15

Или вы копируете код из 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;
}
Заратустра
источник
1
Это работает даже с 2-символьным юникодом, потому что 1-й символ> = U + D800
k3b
Но обратите внимание, что он включает непечатаемые символы в ASCII (что правильно, но этого нельзя ожидать). Конечно, можно напрямую использовать return falseвместо isASCII = falseи break.
Maarten Bodewes
Это код из Oracle JDK. Копирование может вызвать юридические проблемы.
Arne Deutsch
11

commons-lang3 от Apache содержит ценные служебные / удобные методы для всех видов «проблем», включая этот.

System.out.println(StringUtils.isAsciiPrintable("!@£$%^&!@£$%^"));
fjkjava
источник
1
Имейте в виду, что isAsciiPrintable возвращает false, если строка содержит символы табуляции или перевода строки (\ t \ r \ n).
TampaHaze
@TampaHaze, потому что внутренне он проверяет значение каждого символа от 32 до 127. Я думаю, что это неправильно. Надо проверить от 0 до 127
прашант
1
@therealprashant, если бы имя метода было isAscii, я бы с вами согласился. Но метод, названный isAsciiPrintable, подразумевает, что они могли намеренно исключить символы с 0 по 31.
TampaHaze 01
4

попробуй это:

for (char c: string.toCharArray()){
  if (((int)c)>127){
    return false;
  } 
}
return true;
pforyogurt
источник
«Попробуй это» всегда получает отрицательный голос. Что же это сделать ? Что входит, а что нет? Кстати, получил бы отрицательный голос, потому что вы вдвое увеличиваете объем памяти.
Maarten Bodewes
1

Выполните итерации по строке и используйте charAt () для получения символа. Затем относитесь к нему как к int и посмотрите, есть ли у него значение Unicode (надмножество ASCII), которое вам нравится.

Перерыв на первое, что вам не нравится.

Торбьёрн Равн Андерсен
источник
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
источник
Ответ только на код, укажите, что он делает, т.е. что он включает непечатаемые символы и неопределенный символ (0x7F), если вы выполняете эту проверку.
Maarten Bodewes
Этот, возможно, укусил меня после того, как моя долгая программа не смогла найти интересующих персонажей. charAtвозвращает char. Можете ли вы напрямую проверить, является ли тип charбольше, чем int, сначала без преобразования в int, или ваш тест автоматически выполняет покрытие? Может быть, можно, а может быть? Я пошел вперед и превращал это к междунар так: if ((int)s.charAt(i) > 127). Не уверен, что мои результаты отличаются, но я чувствую себя лучше, если позволю ему поработать. Мы увидим: - \
harperville
0

Это было возможно. Довольно проблема.

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
источник
0

Это вернет true, если String содержит только символы ASCII, и false, если это не так.

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

Если вы хотите удалить не ASCII, вот фрагмент:

if(!Charset.forName("US-ASCII").newEncoder().canEncode(str)) {
                        str = str.replaceAll("[^\\p{ASCII}]", "");
                    }
Майк Оганян
источник
-2
//return is uppercase or lowercase
public boolean isASCIILetter(char c) {
  return (c > 64 && c < 91) || (c > 96 && c < 123);
}
Лукас Гребликас
источник
Код отвечает только с четырьмя магиями и без объяснения, что он делает . Пожалуйста, отрегулируйте.
Maarten Bodewes