2

Допустим, имеется строка. Как определить, что она содержит нерусские символы? Потенциальный текст не содержит специальных символов, кроме пробелов. Конечно, можно было бы проверить наличие того или иного символа, но я думаю, что есть более простой и эффективный способ.

Посоветуйте, как это осуществить максимально быстро?

Pavel Mayorov
  • 58,537
Eugen
  • 707
  • 2
    Пробел, запятая, точка, тире, скобки, кавычки считаются нерусскими? А китайские кавычки (『 』)? А немецкие (»вот такие«)? – VladD Jun 30 '15 at 12:36
  • 3
    "12345" - это русский текст или нет? ;-) – cpp_user Jun 30 '15 at 12:36
  • конечно можно было бы проверить наличие того или иного символа, но я думаю что есть более простой и эффективный способ – Eugen Jun 30 '15 at 12:37
  • еще прикол в том что потенциальный текст не содержит специальных символов, разве что только пробелы. – Eugen Jun 30 '15 at 12:38
  • 2
    @Eugen: Вы всё же ответьте на вопрос, без этого невозможно вам ничего посоветовать. Давайте ещё докину: а белорусская ў? А исконно русский ять ѣ? – VladD Jun 30 '15 at 12:39
  • @VladD: считаются – Eugen Jun 30 '15 at 12:45
  • 2
    но я думаю что есть более простой и эффективный способ - что вас навело на эту мысль? – Nofate Jun 30 '15 at 12:45
  • Извините, на Java не пишу, так что ответ предлагать не стану. Но ведь Java кодирует в UTF-16, да? Создайте битовую карту по всем 65 536 значениям UTF-16, и по ней в цикле смотрите.

    Не понимаю, с какой стати вопрос минуснули. Автор чётко сказал: нерусские символы (т.е. те, которых в русском тексте нет), а не нерусский текст.

    – Incnis Mrsi Aug 26 '15 at 07:58
  • @IncnisMrsi 65535 значений UTF-16? Что вы имеете в виду под "значениями" и почему только 2^16? – Nick Volynkin Jan 09 '17 at 19:17
  • 1
    @VladD ещё хитрый случай придумал. Если к кириллической Е добавить комбинированное двоеточие, символ считается русским? – Nick Volynkin Jan 09 '17 at 19:19
  • @NickVolynkin: Хороший вопрос. По идее, у них должна быть одинаковая каноническая композиция. Но об этом должна заботиться механика регулярных выражений, наверное, а не программист. (Но между «должна заботиться» и «в реальности свалено на программиста» есть хорошая пропасть, конечно.) – VladD Jan 09 '17 at 19:48
  • @VladD ага, я за три недели изучения юникода набрался пессимизма на этот счёт). – Nick Volynkin Jan 09 '17 at 19:52
  • @VladD я даже не поручусь, что все возможные варианты пробелов там описаны) – Nick Volynkin Jan 09 '17 at 19:54
  • @NickVolynkin: Тогда буду следить за вашими ответами, позапоминаю, где там подводные камни :) – VladD Jan 09 '17 at 20:01
  • @VladD кстати, в ответах тоже изрядно заблуждений. С C# так же? (Пример: http://ru.stackoverflow.com/a/407454/181472) – Nick Volynkin Jan 09 '17 at 20:06
  • 1
    @NickVolynkin: Да, в самом начале было практически стандартом игнорировать символы вне BMP, так что UTF-16 считалась кодировкой с фиксированным числом байт на символ. Вот статья Эрика по этому поводу. Хуже того, в C# строковые функции (например, Length) работают в 16-битных словах, а не в code point'ах, так что проблема есть и будет ещё долго. :-( – VladD Jan 09 '17 at 20:13
  • @VladD остается только грустно вздыхать и пиарить utf8everywhere.org) – Nick Volynkin Jan 09 '17 at 20:15
  • @NickVolynkin: Я не вижу особых причин использования utf-8, а не utf-16. У них примерно одинаковый круг проблем, разве что utf-16 бывает little и big endian. Разве есть ещё какие-то преимущества? – VladD Jan 09 '17 at 20:28

3 Answers3

8
public static void main(String[] args) throws Exception {
    Pattern pattern = Pattern.compile(
            "[" +                   //начало списка допустимых символов
                    "а-яА-ЯёЁ" +    //буквы русского алфавита
                    "\\d" +         //цифры
                    "\\s" +         //знаки-разделители (пробел, табуляция и т.д.)
                    "\\p{Punct}" +  //знаки пунктуации
            "]" +                   //конец списка допустимых символов
            "*");                   //допускается наличие указанных символов в любом количестве
    Matcher matcher = pattern.matcher("Посоветуйте, как это осуществить максимально быстро?");
    System.out.println(matcher.matches());//true

    matcher = pattern.matcher("lorem ipsum dolor sit amet");
    System.out.println(matcher.matches());//false
}

Если требуется разрешить еще какие-то символы, следует добавить их между квадратными скобками []. См. также JavaDoc к классу java.util.regex.Pattern

Для других языков можно использовать тот же самый код, заменив "а-яА-ЯёЁ" на готовые регулярные выражения, описывающие алфавиты большинства распространенных языков

bobzer
  • 2,247
  • 1
    Кстати, не факт, что регулярка — это максимально быстро. – Nick Volynkin Aug 06 '15 at 09:01
  • "допускается наличие указанных символов в любом количестве" но хотя бы один-то там должен быть? Может, все же "+", а не "*" ? – mega Aug 06 '15 at 19:43
  • 2
    Может, все же "+" - комментарий к выражению соответствует действию, им выполняемому (* это 0 или более раз, т.е. в любом количестве). Поставленная задача, если коротко: проверить есть в тексте не русские символы или нет. Смена * на + будет решать уже другую задачу - проверить, чтобы в тексте был 1 или более символ русского языка. – bobzer Aug 07 '15 at 09:54
3

Если быстро и строка длинная (что считать длинным это отдельный вопрос), то можно проверить несколько (скажем, 0.1 от длины строки) случайных символов на попадание в диапазон от 'А' до 'я' и если их процент выше заданного, то считать строку русским текстом.
(наверное, для коротких строк быстрее проверять все символы).

avp
  • 46,098
  • 6
  • 48
  • 116
  • Делать классифицирующий алгоритм случайно-зависимым? Не думаю, что это хорошая мысль. «Разрешить или нет… ?» Сейчас, кости только подброшу ☺ – Incnis Mrsi Aug 27 '15 at 15:31
  • @IncnisMrsi, скажите это разработчикам InnoDB: ANALYZE TABLE determines index cardinality... by doing random dives to each of the index trees and updating index cardinality estimates accordingly А ведь кардинальность индекса играет не маленькую роль в оптимизаторе – BOPOH Aug 27 '15 at 15:47
2

Если коротко: явным образом перечислить все искомые символы плюс знаки препинания в регулярном выражении и проверять им строку.

Осталось определить множество русских символов.

Nick Volynkin
  • 34,094
  • Перечислить что, не русские? В Unicode, ага? – Incnis Mrsi Aug 26 '15 at 07:50
  • @IncnisMrsi: конечно нет, перечислить русские и проверять, что каждый символ входит в множество. Прочитайте принятый ответ. – Nick Volynkin Aug 26 '15 at 07:52
  • Смысл? Не разбираюсь в Жаве, чтобы судить о скорости регулярки в сравнении с циклами. – Incnis Mrsi Aug 26 '15 at 08:01
  • @IncnisMrsi: в принятом ответе реализуется то, что я описал словами в этом. Раз он вызвал у вас сомнения, можете посмотреть на реализацию. Регулярка там видна и без знания Java. – Nick Volynkin Aug 26 '15 at 08:03
  • Знаю, что Вас раздражаю, но семантика слова искомый не соответствует тому, о чём Вы думаете. – Incnis Mrsi Aug 26 '15 at 08:05
  • @IncnisMrsi: в данном случае искомыми символами являются именно символы русского алфавита. Мы ищем их в каждом символе, и если хоть в одном не находим, то текст — «не русский». Искать символы множества Unicode \ {символы русского алфавита } не представляется возможным. – Nick Volynkin Aug 26 '15 at 08:08
  • А, в Вашем мире поиск происходит в словаре, а не в строке. Понятия обращены относительно моих. Да, теперь понял, хотя не стал бы думать как о поиске об том, что может быть реализовано путём обращения по адресу (см. мой комментарий). – Incnis Mrsi Aug 26 '15 at 08:22