Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Как делается отрицание слова


Автор: _program_seeker 28.2.2007, 14:54
Здравствуйте, я прочитал про регулярные выражения, поделал некоторые примеры, но более сложные вещи осуществить трудно и непонятно. Помогите пожалуйста составить регулярное выражение, кторое описывает строку, содержащую любые последовательнсти символов кроме указанной цепочки.

Использую следующий паттерн

Код

Pattern p = Pattern.compile("AB([^A]*)AB");
Matcher m = p.matcher("nnnnnnnnnnnnnnnnnnnn"); //проверяемая строка

if(m.find()) {                      //если в строке найден паттерн
   String s = m.group(1);   //получить группу из строки, тоесть вернется то что входит 
                                         //в подпаттерн, описываемый внутри скобок
}



все работает как надо:

nnABnnnnnnnnnnABnnnn      - здесь выводит true, потому что строка содержит обрамление AB
nnnnnnnnnnnnnnnnnnnnnn  - здесь выводит false, потому что обрамления нет
nnABnnnnnAnnnnnABnn        - здесь выводит false, потому что внутри обрамления есть запрещенный символ A

вот... а мне теперь нужно чтобы был не просто запрещенный символ A а запрещенная цепочка AB. и всвязи с этим возникло затруднение.. как указать в паттерне отрицание слова AB ?

Автор: DEER 28.2.2007, 15:42
AB([^(AB)]*)AB


Автор: _program_seeker 28.2.2007, 15:52
к сожалению паттерн AB([^(AB)]*)AB реагирует и на такую строку smile

ABnnnnnAnnnnnnnnnnnnnnnnnAB

Автор: DEER 28.2.2007, 16:08
ну да... там же нет AB в середине строки..
а ловятся только AB целиком

если надо не A и не B, то
AB([^AB]*)AB

Автор: w1nd 28.2.2007, 16:15
AB([^\\QAB\\E]*)AB

Цитата(DEER @  28.2.2007,  16:08 Найти цитируемый пост)
если надо не A и не B, тоAB([^AB]*)AB

И [^AB], и [^(AB)] означает "не (A или B)". 

Автор: _program_seeker 28.2.2007, 17:31
Цитата

AB([^\\QAB\\E]*)AB



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


как найти строки, не содержащие слово водка?

например:



"это простое предложение про огурец" - эта строка правильная

"водка это напиток богов" - эта строка неправильная

Автор: nornad 28.2.2007, 17:48
Вообще-то, и AB([^\\QAB\\E]*)AB тоже означает не (А или В) - в классе символов описываются символы, а не последовательности.
Специально проверил ваш шаблон:
Код

строка: abdbnabdanab
шаблон: "ab([^\\Qab\\E]*)ab"
результат: не найдено совпадений

строка: abdbnbdanab
шаблон: "ab([^\\Qab\\E]*)ab"
результат: не найдено совпадений

Разница в строках всего в один символ.

Добавлено @ 17:50 
Цитата(_program_seeker @  28.2.2007,  17:31 Найти цитируемый пост)
как найти строки, не содержащие слово водка?

Если инвертировать подход к решению, то задача решается проще некуда. Просто найди все строки, которые содержат запрещённые слова.

Автор: _program_seeker 28.2.2007, 18:04
Цитата

Если инвертировать подход к решению, то задача решается проще некуда. Просто найди все строки, которые содержат запрещённые слова.


ну а прямого решения выходит не существует?

Автор: nornad 28.2.2007, 18:25
Не могу гарантировать на 100%, но я не знаю, как можно организовать отрицание некоторой последовательности. smile 

Автор: _program_seeker 28.2.2007, 18:31
всем откликнувшимся большое спасибо за участие! ваши примеры помогли мне и навели на такое решение! которое по моему делает то что мне надо:

Код

AB([^A][^B]*?)AB


буду тестировать нет ли тут подводных камней smile

Автор: DEER 28.2.2007, 18:34
млин точно!

Автор: _program_seeker 28.2.2007, 18:52
только как я и предполагал, тут обнаружился неприятный подводный камень:

паттерн: AB([^A][^B]*?)AB
строка: ABoooooooooooooooooAB 
результат: соответствует шаблону

строка: ABoooooooABooooooooooAB
результат: несоответствует шаблону


и вот он подводный камень!
строка: ABoooooooAooBooooooooooAB
результат: несоответствует шаблону                                    (хотя по задумке должен)

и вот камень
строка: ABoooooooBAooooooooooAB
результат: несоответствует шаблону                                    (просто поменяли местами A и B)

Автор: nornad 28.2.2007, 19:41
Даже ABoooooooAooooooooooAB будет с "камнем". Потому что шаблон означает следующее:
"проверить, начинается ли строка с АВ, заканчивается ли АВ и при этом не содержит ни А, ни В внутри".

Автор: w1nd 28.2.2007, 23:24
Цитата(nornad @  28.2.2007,  17:48 Найти цитируемый пост)
Вообще-то, и AB([^\\QAB\\E]*)AB тоже означает не (А или В)

Правильно, это означает "не AB". И отлично работает для "ABoooooooooooooooooAB", "ABoooooooAooBooooooooooAB" и "ABoooooooBAooooooooooAB". Строка "ABoooooooABooooooooooAB", разумеется, не подходит, так как содержит "AB".

А вот шаблон "AB([^A][^B]*?)AB" неверен (если исходить из первоначальных условий), потому что последовательность "[^A][^B]" означает "любые два символа не являющиеся A или B". А слово водка в предложении найдет вот такой шаблон: ".*\\QВОДКА\\E.*"


Автор: nornad 28.2.2007, 23:38
Цитата(w1nd @  28.2.2007,  23:24 Найти цитируемый пост)
И отлично работает для

Уверен? Проверял? Вот что говорит тестирование шаблона AB([^\\QAB\\E]*)AB на приведённых строках:
Код

ABoooooooooooooooooAB
совпадает (найдено: ABoooooooooooooooooAB)

ABoooooooAooBooooooooooAB
не совпадает

ABoooooooBAooooooooooAB
не совпадает

ABoooooooABooooooooooAB
совпадает (надено: ABoooooooAB)

Может, суть задачи не до конца ясна? Надо проверить строку на то, что в ней не содержится непрерывная последовательность АВ, а не просто два символа А и В. Причём, я бы в шаблон добавил ^$, иначе сами видите, что получается (последняя строка в примере).

Автор: w1nd 28.2.2007, 23:45
Цитата(nornad @  28.2.2007,  23:38 Найти цитируемый пост)
Уверен? Проверял?

Абсолютно уверен. Проверял.

Цитата(nornad @  28.2.2007,  23:38 Найти цитируемый пост)
Может, суть задачи не до конца ясна? Надо проверить строку на то, что в ней не содержится непрерывная последовательность АВ, а не просто два символа А и В.

Предельно ясна. Выражение "\\QAB\\E" как раз и обозначает последовательность "AB".

Код примера:
Код
public class RegexTest {

    public static void main(String... args) {
        String regex = "AB([^\\QAB\\E]*)AB";
        
        test("ABoooooooooooooooooAB", regex);
        test("ABoooooooAooBooooooooooAB", regex);
        test("ABoooooooBAooooooooooAB", regex);
        test("ABoooooooABooooooooooAB", regex);
    }
    
    static void test(String text, String regex) {
        System.out.printf(
            "Текст = \"%s\" %s для \"%s\"\r\n", 
            text, 
            text.matches(regex) ? "подходит" : "не подходит", 
            regex);
    }
    
}


Вывод (java 1.5.0_01):
Код
Текст = "ABoooooooooooooooooAB" подходит для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooAooBooooooooooAB" подходит для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooBAooooooooooAB" подходит для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooABooooooooooAB" не подходит для "AB([^\QAB\E]*)AB"

Автор: nornad 1.3.2007, 01:55
Странно... У меня твой пример даёт такой вот результат:
Код

Текст = "ABoooooooooooooooooAB" подходит для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooAooBooooooooooAB" не подходит для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooBAooooooooooAB" не подходит для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooABooooooooooAB" не подходит для "AB([^\QAB\E]*)AB"

Проверял на jdk6.0

Добавлено @ 01:57 
По идее, внутри [] по правилам регулярных выражений указывается символьный класс, соответствующий одному символу. Не представляю, как у тебя твой пример смог обработать символьный класс как последовательность...

Автор: corpsehunter 1.3.2007, 02:49
Цитата(nornad @ 1.3.2007,  01:55)
Странно... У меня твой пример даёт такой вот результат:

Ничего странного, у меня такой же smile 
Вообще, выражение [^\\QAB\\E] значит любой из символов, кроме \ Q A B \ E  smile И бэкслэш вовсе не обязательно указывать дважды smile  smile 

Автор: nornad 1.3.2007, 04:34
Цитата(corpsehunter @  1.3.2007,  02:49 Найти цитируемый пост)
И бэкслэш вовсе не обязательно указывать дважды

Конечно. Скомпилирую в жабе простейший исходник:
Код

public class RegexTest {
    public static void main(String... args) {
        String regex = "AB([^\QAB\E]*)AB";
    }
}

И посмотри, что тебе скажет жаба. ;)

Добавлено @ 04:42 
corpsehunter, кстати, я не совсем понял, что ты хотел сказать словами
Цитата(corpsehunter @  1.3.2007,  02:49 Найти цитируемый пост)
Вообще, выражение [^\\QAB\\E] значит любой из символов, кроме \ Q A B \ E

Это ты мне объяснял или w1nd'у?
\QAB\E - любая последовательность не АВ
[\QAB\E] - любой один символ, кроме А и В.
А мои слова относились к тому, что я не понимаю, каким образом на jdk1.5.0_01 это дело работало не как символьный класс, а как последовательность.

Добавлено @ 04:45 
Всё, дошло, что ты имел в виду. smile
Если ты заметил, в коде [^\\QAB\\E] взято в кавычки.  smile  А в тексте для упрощения пишем часто без кавычек.

Автор: w1nd 1.3.2007, 13:08
Цитата(nornad @  1.3.2007,  01:55 Найти цитируемый пост)
По идее, внутри [] по правилам регулярных выражений указывается символьный класс, соответствующий одному символу. Не представляю, как у тебя твой пример смог обработать символьный класс как последовательность...

Да, с символьным типом я обмишурился. Значит в пятёрке баг в реализации regexp...

Автор: corpsehunter 1.3.2007, 15:10
Цитата(nornad @ 1.3.2007,  04:34)

\QAB\E - любая последовательность не АВ

Т.е. это тоже самое, что и [^A][^B]?
Цитата(nornad @ 1.3.2007,  04:34)

[\QAB\E] - любой один символ, кроме А и В.

А это - [^AB]?

Просто в первом примере стоял двойной бэкслэш, а это в регулярном выражении значит одинарный слэш, а тут ты уже поставил одинарный smile
Да и вообще, я раньше не слышал про \Q...\E, шаз прочитал в доке - там написано, что все, что заключено в них просто экранируется бэкслэшем. Че-то несостыковочка какая-то...

Автор: _program_seeker 1.3.2007, 17:34
w1nd, спасибо, твой пример я проверил на свежую голову и действительно все работает! я немного изменил только твою программу, и добавил пару тестовых строк, и вот что получилось:

программа (jdk_1.5.0_8):
Код

public static void main(String[] args) {

        String regex = "AB([^\\QAB\\E]*)AB";

        String t = "";
        test(t + "ABooooooooooooooooooooAB" + t, regex);
        test(t + "ABoooooooAooBoooooooooAB" + t, regex);
        test(t +"ABoooooooBAoooooooooooAB" + t, regex);
        test(t +"ABoooooooBooAoooooooooAB" + t, regex);
        test(t +"ABooooooooooAoooooooooAB" + t, regex);
        test(t +"ABooooooooooBoooooooooAB" + t, regex);
        test(t +"ABoooooooABoooooooooooAB" + t, regex);
        test(t +"ooooooooooooooooooooooooo" + t, regex);
}

static void test(String text, String regex) {
        System.out.printf(
                "Текст = \"%s\" %s для \"%s\"\r\n",
                text,
                text.matches(regex) ? "\t\tподходит\t" : "\t\tне подходит\t",
                regex);
}


я добавил переменную t для того чтобы можно было обрамлять строки теста дополнительными незначащими символами, для чего это сделано - будет видно чуть позже.
Вначале я провел тест при значении переменной t = "", и вот результаты:
Код

Текст = "ABooooooooooooooooooooAB"        подходит     для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooAooBoooooooooAB"        подходит     для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooBAoooooooooooAB"        подходит     для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooBooAoooooooooAB"        подходит     для "AB([^\QAB\E]*)AB"
Текст = "ABooooooooooAoooooooooAB"        подходит     для "AB([^\QAB\E]*)AB"
Текст = "ABooooooooooBoooooooooAB"        подходит     для "AB([^\QAB\E]*)AB"
Текст = "ABoooooooABoooooooooooAB"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooooooooooooooooooooooooo"        не подходит     для "AB([^\QAB\E]*)AB"

на этом наборе тестов паттерн, предложенный w1nd, работает превосходно.
а вот когда я провел тесты но при значении переменной t = "oo" то получил результаты которые не поддаются логике:

Код

Текст = "ooABooooooooooooooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooABoooooooAooBoooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooABoooooooBAoooooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooABoooooooBooAoooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooABooooooooooAoooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooABooooooooooBoooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooABoooooooABoooooooooooABoo"        не подходит     для "AB([^\QAB\E]*)AB"
Текст = "ooooooooooooooooooooooooooooo"    не подходит     для "AB([^\QAB\E]*)AB"


почему так? ведь паттерн не ограничивается строкой, в нем нет начального ^ и завершающего $ символа?

Автор: nornad 1.3.2007, 18:04
Цитата(corpsehunter @  1.3.2007,  15:10 Найти цитируемый пост)
Т.е. это тоже самое, что и [^A][^B]?

Нет. [^A][^B] - два последовательных символа, но первый не А, а второй не В.
\QAB\E - последовательность любой величины, лишь бы это не была последовательность АВ.

Цитата(corpsehunter @  1.3.2007,  15:10 Найти цитируемый пост)
Цитата(nornad @ 1.3.2007,  04:34)
[\QAB\E] - любой один символ, кроме А и В.

А это - [^AB]?

Да.

Цитата(_program_seeker @  1.3.2007,  17:34 Найти цитируемый пост)
почему так? ведь паттерн не ограничивается строкой, в нем нет начального ^ и завершающего $ символа?

Потому что String.matches работает не совсем так, как обычный паттерн-матчинг. Часто он считает, что твой шаблон должен подходить именно для всей строки. Я бы сказал, что это всегда так, но сам пару раз столкнулся с необходимостью явно указать ^$.
Кстати, раз уж ты пишешь в пятой жабе, то учти, что этот же код в шестой жабе уже будет работать иначе. О причине читай выше.

Добавлено @ 18:06 
Хотя, насчёт \QAB\E могу ошибаться - мало сталкивался.

Автор: corpsehunter 1.3.2007, 18:24
У меня в первом случае все равно не так получается. Во втором так же. Версия 1.6.0
И вообще, не понимаю, почему у тебя так получается и откуда взяли, что \Q…\E обозначает последовательность:
Цитата

\Q…\E - обозначает, что все метасимволы в регулярном выражении между \Q и \E экранируются при помощи символа “\”;

P.S. Со количеством слэшэй я накосячил - перепутал с пхп - там используется одиночный...

Автор: nornad 1.3.2007, 21:50
Спасибо, что напомнил. Итого получаем, что \QАВ\E === АВ, а [^\QАВ\E] === [^АВ].

Автор: sova7 2.3.2007, 11:24
Насчет поиска фраз "без слова водка" - так понял, решение не найдено ?
Из решений обратной задачи для "ABooAooBooAB" можно предложить 
Код

        String regex = "(AB.*){3}";
        
        String[] ar = new String[]{
             "ABoooooooooooooooooAB",
             "ABoooooooAooBooooooooAB",
             "ABoooooooBAooooooooooAB",
             "ABoooooooABooooooooooAB"};

        for (int i = 0; i < ar.length; i++) {
         String text = ar[i];
         System.out.println(text + "\t\t" + text.matches(regex));
        }

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)