Что такое граница слова в регулярном выражении?

138

Я использую регулярные выражения Java в Java 1.6 (помимо прочего, для анализа числового вывода) и не могу найти точное определение \b(«граница слова»). Я предполагал, что -12это будет «целое слово» (соответствует \b\-?\d+\b), но оказалось, что это не работает. Я был бы признателен за информацию о способах сопоставления чисел, разделенных пробелами.

Пример:

Pattern pattern = Pattern.compile("\\s*\\b\\-?\\d+\\s*");
String plus = " 12 ";
System.out.println(""+pattern.matcher(plus).matches());
String minus = " -12 ";
System.out.println(""+pattern.matcher(minus).matches());
pattern = Pattern.compile("\\s*\\-?\\d+\\s*");
System.out.println(""+pattern.matcher(minus).matches());

Это возвращает:

true
false
true
peter.murray.rust
источник
Можете ли вы опубликовать небольшой пример с вводом и ожидаемым выводом?
Брент пишет код
Пример Pattern pattern = Pattern.compile ("\\ s * \\ b \\ -? \\ d + \\ s *"); Строка плюс = "12"; System.out.println ( "" + pattern.matcher (плюс) .matches ()); Строка минус = "-12"; System.out.println ( "" + pattern.matcher (минус) .matches ()); pattern = Pattern.compile ("\\ s * \\ -? \\ d + \\ s *"); System.out.println ( "" + pattern.matcher (минус) .matches ()); дает: true false true
peter.murray.rust

Ответы:

98

Граница слова в большинстве диалектов регулярных выражений - это позиция между \wи \W(символ, не являющийся словом), или в начале или конце строки, если она начинается или заканчивается (соответственно) символом слова ( [0-9A-Za-z_]).

Таким образом, в строке "-12"он будет соответствовать перед 1 или после 2. Тире не является символом слова.

brianary
источник
35
Correctamundo. \b- это утверждение нулевой ширины, которое соответствует, если оно есть \wна одной стороне, и либо есть \Wна другой, либо позиция является началом или концом строки. \wпроизвольно определяется как символы "идентификатора" (alnums и подчеркивание), а не как что-то особенно полезное для английского языка.
hobbs
100% правильно. Приносим извинения за то, что не только прокомментировали ваше. Я нажал "Отправить", прежде чем увидел ваш ответ.
Брент пишет код
5
ради понимания, можно переписать регулярное выражение \bhello\bбез использования \b(используя \w, \Wи другие)?
Дэвид Портабелла
5
Типа:, (^|\W)hello($|\W)за исключением того, что он не будет захватывать никакие символы, не являющиеся словами, до и после, поэтому это будет больше похоже (^|(?<=\W))hello($|(?=\W))(с использованием утверждений просмотра вперед / назад).
brianary
7
@brianary Немного проще: (?<!\w)hello(?!\w).
Дэвид Книп
28

Граница слова может находиться в одном из трех положений:

  1. Перед первым символом в строке, если первый символ является символом слова.
  2. После последнего символа в строке, если последний символ является символом слова.
  3. Между двумя символами в строке, где один символ слова, а другой не символ слова.

Символы слова являются буквенно-цифровыми; знака минус нет. Взято из учебника Regex .

WolfmanDragon
источник
21

В процессе изучения регулярных выражений я действительно застрял в метасимволе, которым является \b. Я действительно не понимал его значения, когда я неоднократно спрашивал себя « что это такое, что это такое ». После нескольких попыток использования веб-сайта я наблюдаю за розовыми вертикальными черточками в каждом начале слова и в конце слов. Тогда я хорошо понял его смысл. Теперь это точно слово ( \w) -ограничение .

Моя точка зрения исключительно ориентирована на понимание. Логику этого следует исследовать из других ответов.

введите описание изображения здесь

ОСШ
источник
3
Очень хороший сайт, чтобы понять, что такое граница слов и как происходят
совпадения
2
Этот пост заслуживает похвалы за показ, а не за рассказ. Картинка стоит тысячи слов.
M_M
13

Граница слова - это позиция, которой либо предшествует символ слова и не следует за ним, либо за которой следует символ слова и не предшествует один.

Алан Мур
источник
8

Я говорю о том, какие \bграницы регулярных выражений здесь на самом деле .

Короче говоря, они условны . Их поведение зависит от того, с чем они рядом.

# same as using a \b before:
(?(?=\w) (?<!\w)  | (?<!\W) )

# same as using a \b after:
(?(?<=\w) (?!\w)  | (?!\W)  )

Иногда это не то, что вам нужно. См. Мой другой ответ для уточнения.

tchrist
источник
8

Я хотел бы объяснить ответ Алана Мура

Граница слова - это позиция, которой либо предшествует символ слова и не следует за ним, либо за которой следует символ слова и не предшествует один.

Предположим, у меня есть строка «This is a c a t, and she is a wesome», и я должен заменить все вхождения на букву «a», только если эта буква существует на «границе слова» , т. Е. букву aвнутри «кота» заменять не нужно.

Поэтому я буду выполнять регулярное выражение (в Python ) как

re.sub("\ba","e", myString.strip())// заменить aнаe

поэтому выход будет это eс в т - eе она ewesome

Дакша Гаргас
источник
5

Я столкнулся с еще худшей проблемой при поиске текста для слов , как .NET, C++, C#, и C. Вы могли бы подумать, что программисты лучше знают, чем называть язык тем, для чего трудно писать регулярные выражения.

Во всяком случае, вот что я узнал (в основном это обобщено на http://www.regular-expressions.info , это отличный сайт): в большинстве разновидностей регулярных выражений символы, которые соответствуют классу сокращенных символов, \wявляются символы, которые рассматриваются как символы слова границами слов. Java - исключение. Java поддерживает Unicode для, \bно не для \w. (Я уверен, что в то время для этого была веская причина).

Это \wозначает «словесный символ». Он всегда соответствует символам ASCII [A-Za-z0-9_]. Обратите внимание на наличие символа подчеркивания и цифр (но не тире!). В большинстве разновидностей, поддерживающих Unicode, \wвключает много символов из других скриптов. Есть много противоречий в том, какие символы фактически включены. Буквы и цифры из алфавитных шрифтов и идеограмм обычно включаются. Знаки препинания соединителя, кроме подчеркивания и числовых символов, которые не являются цифрами, могут или не могут быть включены. XML Schema и XPath даже включают все символы в \w. Но Java, JavaScript и PCRE соответствуют только символам ASCII с \w.

Именно поэтому Java на основе регулярных выражений для поиска C++, C#или .NET(даже если вы помните , чтобы избежать периода и плюсов) вкручиваются самым \b.

Примечание: я не уверен, что делать с ошибками в тексте, например, когда кто-то не ставит пробел после точки в конце предложения. Я допустил это, но не уверен, что это обязательно правильно.

В любом случае, в Java, если вы ищете текст для этих странно названных языков, вам нужно заменить \bпробелы и знаки препинания до и после. Например:

public static String grep(String regexp, String multiLineStringToSearch) {
    String result = "";
    String[] lines = multiLineStringToSearch.split("\\n");
    Pattern pattern = Pattern.compile(regexp);
    for (String line : lines) {
        Matcher matcher = pattern.matcher(line);
        if (matcher.find()) {
            result = result + "\n" + line;
        }
    }
    return result.trim();
}

Затем в вашей тестовой или основной функции:

    String beforeWord = "(\\s|\\.|\\,|\\!|\\?|\\(|\\)|\\'|\\\"|^)";   
    String afterWord =  "(\\s|\\.|\\,|\\!|\\?|\\(|\\)|\\'|\\\"|$)";
    text = "Programming in C, (C++) C#, Java, and .NET.";
    System.out.println("text="+text);
    // Here is where Java word boundaries do not work correctly on "cutesy" computer language names.  
    System.out.println("Bad word boundary can't find because of Java: grep with word boundary for .NET="+ grep("\\b\\.NET\\b", text));
    System.out.println("Should find: grep exactly for .NET="+ grep(beforeWord+"\\.NET"+afterWord, text));
    System.out.println("Bad word boundary can't find because of Java: grep with word boundary for C#="+ grep("\\bC#\\b", text));
    System.out.println("Should find: grep exactly for C#="+ grep("C#"+afterWord, text));
    System.out.println("Bad word boundary can't find because of Java:grep with word boundary for C++="+ grep("\\bC\\+\\+\\b", text));
    System.out.println("Should find: grep exactly for C++="+ grep(beforeWord+"C\\+\\+"+afterWord, text));

    System.out.println("Should find: grep with word boundary for Java="+ grep("\\bJava\\b", text));
    System.out.println("Should find: grep for case-insensitive java="+ grep("?i)\\bjava\\b", text));
    System.out.println("Should find: grep with word boundary for C="+ grep("\\bC\\b", text));  // Works Ok for this example, but see below
    // Because of the stupid too-short cutsey name, searches find stuff it shouldn't.
    text = "Worked on C&O (Chesapeake and Ohio) Canal when I was younger; more recently developed in Lisp.";
    System.out.println("text="+text);
    System.out.println("Bad word boundary because of C name: grep with word boundary for C="+ grep("\\bC\\b", text));
    System.out.println("Should be blank: grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));
    // Make sure the first and last cases work OK.

    text = "C is a language that should have been named differently.";
    System.out.println("text="+text);
    System.out.println("grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));

    text = "One language that should have been named differently is C";
    System.out.println("text="+text);
    System.out.println("grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));

    //Make sure we don't get false positives
    text = "The letter 'c' can be hard as in Cat, or soft as in Cindy. Computer languages should not require disambiguation (e.g. Ruby, Python vs. Fortran, Hadoop)";
    System.out.println("text="+text);
    System.out.println("Should be blank: grep exactly for C="+ grep(beforeWord+"C"+afterWord, text));

PS Мои благодарности http://regexpal.com/, без которого мир регулярных выражений был бы очень жалким!

Tihamér
источник
Я изо всех сил пытался понять, почему я не смог соответствовать, C#но теперь стало яснее
Мугома Дж. Окомба
4

Ознакомьтесь с документацией по граничным условиям:

http://java.sun.com/docs/books/tutorial/essential/regex/bounds.html

Посмотрите этот образец:

public static void main(final String[] args)
    {
        String x = "I found the value -12 in my string.";
        System.err.println(Arrays.toString(x.split("\\b-?\\d+\\b")));
    }

Когда вы распечатываете его, обратите внимание, что на выходе получается следующее:

[Я нашел значение - в своей строке.]

Это означает, что символ «-» не воспринимается как находящийся на границе слова, потому что он не считается символом слова. Похоже, @brianary как бы опередил меня, поэтому он получил голосование "за".

Брент пишет код
источник
2

Граница слова \ b используется там, где одно слово должно быть символом слова, а другое - несловесным символом. Регулярное выражение для отрицательного числа должно быть

--?\b\d+\b

проверить рабочий DEMO

Анубхав Шакья
источник
1

Я считаю, что ваша проблема связана с тем, что -это не словесный символ. Таким образом, граница слова будет соответствовать после -, и поэтому не будет захватывать ее. Границы слов совпадают перед первым и после последнего символа слова в строке, а также в любом месте, где перед ним стоит символ слова или не-слово, а после - наоборот. Также обратите внимание, что граница слова соответствует нулевой ширине.

Одна из возможных альтернатив -

(?:(?:^|\s)-?)\d+\b

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

Шон
источник
0

Я думаю, что это граница (то есть следующий за символом) последнего совпадения или начало или конец строки.


источник
1
Вы думаете о \G: соответствует началу строки (например \A) при первой попытке сопоставления; после этого он соответствует позиции, на которой закончился предыдущий матч.
Алан Мур,
0

при использовании \\b(\\w+)+\\bэто означает точное совпадение со словом, содержащим только символы слова([a-zA-Z0-9])

в вашем случае, например, настройка \\bв начале регулярного выражения примет -12(с пробелом), но снова не примет -12(без пробела)

для справки в поддержку моих слов: https://docs.oracle.com/javase/tutorial/essential/regex/bounds.html

Vic
источник