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


120

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

Ответы:


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

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-символа в строке


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.


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("!@£$%^&!@£$%^"));

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;

«Попробуй это» всегда получает отрицательный голос. Что же это сделать ? Что входит, а что нет? Кстати, получил бы отрицательный голос, потому что вы вдвое увеличиваете объем памяти.
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;
}

Ответ только на код, укажите, что он делает, т.е. что он включает непечатаемые символы и неопределенный символ (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;
    }
}

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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.