Co to jest granica słowa w wyrażeniu regularnym?

146

Używam wyrażeń regularnych Java w Javie 1.6 (między innymi do analizowania danych liczbowych) i nie mogę znaleźć dokładnej definicji \b(„granica słowa”). Zakładałem, że -12będzie to „słowo całkowite” (dopasowane przez \b\-?\d+\b), ale wygląda na to, że to nie działa. Byłbym wdzięczny za poznanie sposobów dopasowywania liczb oddzielonych spacjami.

Przykład:

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

To zwraca:

true
false
true
peter.murray.rust
źródło
Czy możesz opublikować mały przykład z danymi wejściowymi i oczekiwanymi wynikami?
Brent pisze kod
Przykładowy wzorzec wzorca = Wzorzec. Stos ("\\ s * \\ b \\ -? \\ d + \\ s *"); Ciąg plus = „12”; System.out.println ("" + pattern.matcher (plus) .matches ()); Ciąg minus = „-12”; System.out.println ("" + pattern.matcher (minus) .matches ()); pattern = Pattern.compile ("\\ s * \\ -? \\ d + \\ s *"); System.out.println ("" + pattern.matcher (minus) .matches ()); daje: prawda fałsz prawda
peter.murray.rust

Odpowiedzi:

102

W większości dialektów wyrażeń regularnych granica słowa to pozycja między znakiem \wa \W(znak niebędący słowem) lub na początku lub na końcu łańcucha, jeśli zaczyna się lub kończy (odpowiednio) znakiem słowa ( [0-9A-Za-z_]).

Tak więc w ciągu "-12"pasowałoby przed 1 lub po 2. Myślnik nie jest znakiem słowa.

brianary
źródło
35
Correctamundo. \bjest asercją o zerowej szerokości, która pasuje, jeśli występuje \wpo jednej stronie, a albo \Wpo drugiej, albo pozycja jest początkiem lub końcem łańcucha. \wjest arbitralnie definiowany jako znaki „identyfikujące” (alnum i podkreślenie), a nie jako coś szczególnie przydatnego w języku angielskim.
hobbs
100% poprawne. Przepraszamy za nie tylko komentowanie twojego. Kliknąłem Prześlij, zanim zobaczyłem twoją odpowiedź.
Brent pisze kod
5
ze względu na zrozumienie, jest to możliwe do przerobienia regex \bhello\bbez używania \b(za pomocą \w, \Wi inne)?
David Portabella
5
Coś w rodzaju: (^|\W)hello($|\W)z wyjątkiem tego, że nie przechwytuje żadnych znaków niebędących słowami przed i po, więc byłoby bardziej jak (^|(?<=\W))hello($|(?=\W))(używając asercji lookahead / lookbehind).
brianary
7
@brianary Nieco prostsza: (?<!\w)hello(?!\w).
David Knipe
28

Granica słowa może występować w jednej z trzech pozycji:

  1. Przed pierwszym znakiem w ciągu, jeśli pierwszy znak jest znakiem słowa.
  2. Po ostatnim znaku w ciągu, jeśli ostatni znak jest znakiem słowa.
  3. Między dwoma znakami w ciągu, gdzie jeden jest znakiem słowa, a drugi nie jest znakiem słowa.

Znaki słów są alfanumeryczne; znak minus nie jest. Zaczerpnięte z samouczka Regex .

WolfmanDragon
źródło
27

Podczas nauki wyrażeń regularnych naprawdę utknąłem w metaznaku, którym jest \b. Rzeczywiście, nie pojmowałem jego znaczenia, kiedy powtarzalnie zadawałem sobie pytanie „ co to jest, co to jest ”. Po kilku próbach korzystania ze strony internetowej zwracam uwagę na różowe pionowe kreski na każdym początku i na końcu słów. Rozumiem, że to dobrze w tamtym czasie. Teraz jest to dokładnie słowo ( \w) -graniczne .

Mój pogląd jest po prostu niezmiernie zorientowany na zrozumienie. Logikę, która za tym stoi, należy zbadać na podstawie innych odpowiedzi.

wprowadź opis obrazu tutaj

snr
źródło
3
Bardzo dobra strona, aby zrozumieć, czym jest granica słów i jak przebiegają dopasowania
vsingh
3
Ten post zasługuje na uznanie za pokazywanie zamiast opowiadania. Obraz jest wart tysiąca słów.
M_M
13

Granica słowa to pozycja, która jest albo poprzedzona znakiem słowa i nie następuje po nim, albo następuje po niej znak słowa i nie jest poprzedzona jednym.

Alan Moore
źródło
1
Czy jestem tylko facetem, który ma ochotę rozwiązać zagadkę, czytając odpowiedź, nawet po latach?
snr
9

Mówię o tym, co \bgranice -Style regex rzeczywiście są tutaj .

Krótko mówiąc, jest to warunkowe . Ich zachowanie zależy od tego, co jest obok.

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

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

Czasami nie tego chcesz. Zobacz moją drugą odpowiedź do rozwinięcia.

tchrist
źródło
8

Chciałbym wyjaśnić, Alan Moore „s odpowiedź

Granica słowa to pozycja, która jest poprzedzona znakiem słowa, a nie następuje po niej lub następuje po niej znak słowa i nie jest poprzedzona jednym.

Załóżmy, że mam ciąg „To jest c t, a ona wesome”, a ja powinien zastąpić wszystkie wystąpienia (S) literę „a” tylko wtedy, gdy ten list istnieje na „granicy słowa” , czyli litery wewnątrz słowa „kot” nie należy zastępować.a

Więc wykonam regex (w Pythonie ) jako

re.sub("\ba","e", myString.strip())// zastąpić aze

więc wyjście będzie to ec na t end ona ewesome

Daksh Gargas
źródło
5

Wpadłem na jeszcze gorszym problemem podczas wyszukiwania tekstu na słowa takie jak .NET, C++, C#, i C. Można by pomyśleć, że programiści komputerowi wiedzieliby lepiej, niż nazywać język czymś, dla którego trudno jest pisać wyrażenia regularne.

Tak czy inaczej, oto co się dowiedziałem (podsumowane głównie z http://www.regular-expressions.info , która jest świetną stroną): W większości odmian wyrażenia regularnego znaki, które są dopasowywane przez klasę znaków krótkiej ręki \wto znaki, które są traktowane jako znaki słowa według granic słów. Java jest wyjątkiem. Java obsługuje Unicode dla, \bale nie dla \w. (Jestem pewien, że wtedy był ku temu dobry powód).

\wOznacza „charakteru słowa”. Zawsze pasuje do znaków ASCII [A-Za-z0-9_]. Zwróć uwagę na podkreślenie i cyfry (ale nie myślnik!). W większości wersji obsługujących Unicode \wzawiera wiele znaków z innych skryptów. Istnieje wiele niespójności co do tego, które postacie są faktycznie uwzględnione. Zwykle uwzględniane są litery i cyfry ze skryptów alfabetycznych i ideogramów. Interpunkcja łącznika, inna niż podkreślenie i symbole numeryczne, które nie są cyframi, może, ale nie musi, zostać uwzględniona. Schemat XML i XPath zawierają nawet wszystkie symbole w \w. Ale Java, JavaScript i PCRE dopasowują tylko znaki ASCII z \w.

Dlatego Java regex wyszukuje C++, C#lub .NET(nawet jeśli pamiętać, aby uciec od okresu i plusy) przykręcone są przez \b.

Uwaga: nie jestem pewien, co zrobić z błędami w tekście, na przykład gdy ktoś nie wstawia spacji po kropce na końcu zdania. Pozwoliłem na to, ale nie jestem pewien, czy koniecznie jest to właściwe postępowanie.

W każdym razie, w Javie, jeśli szukasz tekstu dla tych dziwnie nazwanych języków, musisz zamienić na \bprzed i po białych znakach i znakach interpunkcyjnych. Na przykład:

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

Następnie w teście lub funkcji głównej:

    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 Moje podziękowania dla http://regexpal.com/, bez którego świat regexów byłby bardzo nieszczęśliwy!

Tihamer
źródło
Z trudem próbowałem zrozumieć, dlaczego nie mogłem dopasować, C#ale teraz jest to wyraźniejsze
Mugoma J. Okomba
4

Zapoznaj się z dokumentacją dotyczącą warunków brzegowych:

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

Sprawdź tę próbkę:

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

Podczas drukowania zwróć uwagę, że wynik jest następujący:

[Znalazłem wartość - w moim ciągu.]

Oznacza to, że znak „-” nie jest odbierany jako znajdujący się na granicy słowa, ponieważ nie jest uważany za znak słowa. Wygląda na to, że @brianary trochę mnie pobił, więc dostaje pozytywny głos.

Brent pisze kod
źródło
2

Granica słowa \ b jest używana, gdy jedno słowo powinno być znakiem słowa, a drugie znakiem innym niż słowo. Wyrażenie regularne dla liczby ujemnej powinno wynosić

--?\b\d+\b

sprawdź działające DEMO

Anubhav Shakya
źródło
1

Uważam, że twój problem wynika z faktu, że -nie jest to słowo charakter. W ten sposób granica słowa będzie pasować po znaku -, więc nie będzie go przechwytywać. Granice wyrazów pasują do znaków przed pierwszym i po ostatnim słowie w ciągu, a także w każdym miejscu, w którym przed nim znajduje się znak słowa lub znak niebędący słowem, a po nim jest odwrotnie. Zauważ również, że granica słowa jest dopasowaniem o zerowej szerokości.

Jedną z możliwych alternatyw jest

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

Spowoduje to dopasowanie wszystkich liczb zaczynających się od spacji i opcjonalnego myślnika, a kończących się na granicy słowa. Dopasuje również liczbę zaczynającą się na początku ciągu.

Sean
źródło
0

Myślę, że to granica (tj. Znak następujący) ostatniego dopasowania lub początek lub koniec ciągu.


źródło
1
Myślisz o \G: dopasowuje początek łańcucha (jak \A) przy pierwszej próbie dopasowania; po tym dopasowuje pozycję, na której zakończył się poprzedni mecz.
Alan Moore
0

kiedy używasz \\b(\\w+)+\\btego słowa, oznacza to dokładne dopasowanie do słowa zawierającego tylko znaki słowa([a-zA-Z0-9])

w twoim przypadku np. ustawienie \\bna początku wyrażenia regularnego zaakceptuje -12(ze spacją), ale znowu nie zaakceptuje -12(bez spacji)

w celach informacyjnych na poparcie moich słów: https://docs.oracle.com/javase/tutorial/essential/regex/bounds.html

ofiara
źródło
0

Odniesienie: Opanowanie wyrażeń regularnych (Jeffrey EF Friedl) - O'Reilly

\ b jest równoważne (?<!\w)(?=\w)|(?<=\w)(?!\w)

user4779
źródło