Was ist eine Wortgrenze in Regex?

136

Ich verwende Java-Regexes in Java 1.6 (um unter anderem die numerische Ausgabe zu analysieren) und kann keine genaue Definition von \b("Wortgrenze") finden. Ich hatte angenommen, dass -12dies ein "ganzzahliges Wort" wäre (übereinstimmend mit \b\-?\d+\b), aber es scheint, dass dies nicht funktioniert. Ich wäre dankbar, wenn ich Möglichkeiten kennen würde, durch Leerzeichen getrennte Zahlen abzugleichen.

Beispiel:

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

Dies gibt zurück:

true
false
true
peter.murray.rust
quelle
Können Sie ein kleines Beispiel mit Eingabe und erwarteter Ausgabe veröffentlichen?
Brent schreibt Code
Beispiel 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 ()); gibt: wahr falsch wahr
peter.murray.rust

Antworten:

97

Eine Wortgrenze ist in den meisten Regex-Dialekten eine Position zwischen \wund \W(Nicht-Wort-Zeichen) oder am Anfang oder Ende einer Zeichenfolge, wenn sie mit einem Wortzeichen ( [0-9A-Za-z_]) beginnt oder endet (bzw. endet ).

In der Zeichenfolge "-12"würde es also vor der 1 oder nach der 2 übereinstimmen. Der Bindestrich ist kein Wortzeichen.

brianary
quelle
34
Richtigamundo. \bist eine Behauptung mit einer Breite von Null, die übereinstimmt, wenn sie sich \wauf einer Seite befindet und entweder \Wauf der anderen oder wenn die Position am Anfang oder Ende der Zeichenfolge liegt. \wwird willkürlich als "Bezeichner" (Alnums und Unterstrich) definiert, was für Englisch nicht besonders nützlich ist.
Hobbs
100% richtig. Entschuldigung, dass Sie nicht nur Ihre kommentiert haben. Ich drücke auf "Senden", bevor ich Ihre Antwort sehe.
Brent schreibt Code
5
zum Zweck des Verstehens, ist es möglich , die regex neu zu schreiben , \bhello\bohne \b(mit \w, \Wund andere)?
David Portabella
5
Art von :, (^|\W)hello($|\W)außer dass es vorher und nachher keine Nicht-Wort-Zeichen erfassen würde, also wäre es eher so (^|(?<=\W))hello($|(?=\W))(unter Verwendung von Lookahead / Lookbehind-Behauptungen).
Brian
6
@brianary Etwas einfacher : (?<!\w)hello(?!\w).
David Knipe
28

Eine Wortgrenze kann an einer von drei Positionen auftreten:

  1. Vor dem ersten Zeichen in der Zeichenfolge, wenn das erste Zeichen ein Wortzeichen ist.
  2. Nach dem letzten Zeichen in der Zeichenfolge, wenn das letzte Zeichen ein Wortzeichen ist.
  3. Zwischen zwei Zeichen in der Zeichenfolge, wobei eines ein Wortzeichen und das andere kein Wortzeichen ist.

Wortzeichen sind alphanumerisch; ein Minuszeichen ist nicht. Entnommen aus dem Regex Tutorial .

WolfmanDragon
quelle
21

Während ich den regulären Ausdruck lernte, steckte ich wirklich im Metazeichen fest \b. Ich habe seine Bedeutung tatsächlich nicht verstanden, als ich mich fragte: " Was ist es, was ist es ?" wiederholt ". Nach einigen Versuchen, die Website zu nutzen , achte ich auf die rosa vertikalen Striche an jedem Wortanfang und am Ende von Wörtern. Ich habe es damals gut verstanden. Es ist jetzt genau word ( \w) -gebunden .

Meine Ansicht ist lediglich zu immens verständnisorientiert. Die dahinter stehende Logik sollte anhand anderer Antworten überprüft werden.

Geben Sie hier die Bildbeschreibung ein

snr
quelle
3
Eine sehr gute Seite, um zu verstehen, was eine Wortgrenze ist und wie Übereinstimmungen stattfinden
vsingh
2
Dieser Beitrag verdient Anerkennung für das Zeigen anstatt zu erzählen. Ein Bild sagt mehr als tausend Worte.
M_M
13

Eine Wortgrenze ist eine Position, der entweder ein Wortzeichen vorangestellt und nicht von einem gefolgt wird, oder gefolgt von einem Wortzeichen und nicht von einem vorangestellt.

Alan Moore
quelle
8

Ich spreche darüber, welche \bRegex-Grenzen im Stil tatsächlich hier sind .

Die Kurzgeschichte ist, dass sie bedingt sind . Ihr Verhalten hängt davon ab, neben was sie sich befinden.

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

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

Manchmal ist das nicht das, was du willst. Siehe meine andere Antwort zur Ausarbeitung.

tchrist
quelle
7

Ich möchte die Antwort von Alan Moore erklären

Eine Wortgrenze ist eine Position, der entweder ein Wortzeichen vorangestellt ist und nicht von einem gefolgt wird, oder gefolgt von einem Wortzeichen und nicht von einem vorangestellt.

Angenommen, ich habe eine Zeichenfolge "Dies ist ein C a t, und sie ist eine Wesome", und ich soll alle Vorkommen des Buchstabens 'a' nur ersetzen, wenn dieser Buchstabe an der "Grenze eines Wortes" existiert , d. H. Der Buchstabe ain 'Katze' sollte nicht ersetzt werden.

Also werde ich Regex (in Python ) als ausführen

re.sub("\ba","e", myString.strip())// ersetzen adurche

so dass der Ausgang Dies ist sein ec ein t end sie die ewesome

Daksh Gargas
quelle
5

Ich lief in einem noch schlimmeren Problem , wenn der Text der Suche nach Wörtern wie .NET, C++, C#, und C. Sie würden denken, dass Computerprogrammierer es besser wissen würden, als eine Sprache zu benennen, für die es schwierig ist, reguläre Ausdrücke zu schreiben.

Wie auch immer, das habe ich herausgefunden (hauptsächlich zusammengefasst unter http://www.regular-expressions.info , was eine großartige Seite ist): In den meisten Arten von Regex sind Zeichen, die mit der Kurzzeichen-Zeichenklasse übereinstimmen, \wdie Zeichen, die durch Wortgrenzen als Wortzeichen behandelt werden. Java ist eine Ausnahme. Java unterstützt Unicode für, \baber nicht für \w. (Ich bin sicher, es gab damals einen guten Grund dafür).

Das \wsteht für "Wortzeichen". Es stimmt immer mit den ASCII-Zeichen überein [A-Za-z0-9_]. Beachten Sie die Aufnahme des Unterstrichs und der Ziffern (aber nicht des Bindestrichs!). In den meisten Varianten, die Unicode unterstützen, \wsind viele Zeichen aus anderen Skripten enthalten. Es gibt viele Inkonsistenzen darüber, welche Zeichen tatsächlich enthalten sind. Buchstaben und Ziffern aus alphabetischen Schriften und Ideogrammen sind in der Regel enthalten. Andere Interpunktionszeichen als der Unterstrich und die numerischen Symbole, die keine Ziffern sind, können enthalten sein oder auch nicht. XML Schema und XPath enthalten sogar alle Symbole in \w. Java, JavaScript und PCRE stimmen jedoch nur mit ASCII-Zeichen überein \w.

Aus diesem Grund wird die Java-basierte Regex-Suche nach C++( C#oder .NETauch wenn Sie daran denken, dem Punkt und den Pluspunkten zu entkommen) von der verschraubt\b .

Hinweis: Ich bin mir nicht sicher, was ich gegen Fehler im Text tun soll, z. B. wenn jemand nach einem Punkt am Ende eines Satzes kein Leerzeichen setzt. Ich habe es zugelassen, bin mir aber nicht sicher, ob es unbedingt das Richtige ist.

Wenn Sie in Java nach Text für diese seltsam benannten Sprachen suchen, müssen Sie die \bBezeichner vor und nach Leerzeichen und Satzzeichen ersetzen . Beispielsweise:

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

Dann in Ihrem Test oder Ihrer Hauptfunktion:

    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 Mein Dank geht an http://regexpal.com/, ohne den die Regex-Welt sehr elend wäre!

Tihamer
quelle
Ich versuchte zu verstehen, warum ich nicht C#
mithalten
4

Lesen Sie die Dokumentation zu den Randbedingungen:

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

Schauen Sie sich dieses Beispiel an:

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

Beachten Sie beim Ausdrucken, dass die Ausgabe wie folgt lautet:

[Ich habe den Wert - in meiner Zeichenfolge gefunden.]

Dies bedeutet, dass das Zeichen "-" nicht als an der Grenze eines Wortes befindlich aufgenommen wird, da es nicht als Wortzeichen betrachtet wird. Sieht so aus, als hätte @brianary mich irgendwie geschlagen, also bekommt er eine Gegenstimme.

Brent schreibt Code
quelle
2

Die Wortgrenze \ b wird verwendet, wenn ein Wort ein Wortzeichen und ein anderes ein Nichtwortzeichen sein soll. Regulärer Ausdruck für negative Zahl sollte sein

--?\b\d+\b

Überprüfen Sie die funktionierende DEMO

Anubhav Shakya
quelle
1

Ich glaube, dass Ihr Problem auf die Tatsache zurückzuführen -ist , dass es sich nicht um ein Wortzeichen handelt. Somit stimmt die Wortgrenze nach dem überein -und erfasst sie daher nicht. Wortgrenzen stimmen vor dem ersten und nach dem letzten Wortzeichen in einer Zeichenfolge sowie an jeder Stelle überein, an der es sich um ein Wortzeichen oder ein Nichtwortzeichen handelt und nach dem Gegenteil. Beachten Sie auch, dass die Wortgrenze eine Übereinstimmung mit der Breite Null ist.

Eine mögliche Alternative ist

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

Dies entspricht allen Zahlen, die mit einem Leerzeichen und einem optionalen Bindestrich beginnen und an einer Wortgrenze enden. Es stimmt auch mit einer Zahl überein, die am Anfang der Zeichenfolge beginnt.

Sean
quelle
0

Ich denke, es ist die Grenze (dh die Zeichenfolge) der letzten Übereinstimmung oder der Anfang oder das Ende der Zeichenfolge.


quelle
1
Sie denken an \G: Entspricht dem Anfang der Zeichenfolge (wie \A) beim ersten Übereinstimmungsversuch; Danach entspricht es der Position, an der das vorherige Spiel beendet wurde.
Alan Moore
0

Wenn Sie dies verwenden \\b(\\w+)+\\b, bedeutet dies eine genaue Übereinstimmung mit einem Wort, das nur Wortzeichen enthält([a-zA-Z0-9])

In Ihrem Fall wird beispielsweise die Einstellung \\bzu Beginn von Regex -12(mit Leerzeichen) akzeptiert, aber auch hier nicht -12(ohne Leerzeichen).

Als Referenz zur Unterstützung meiner Worte: https://docs.oracle.com/javase/tutorial/essential/regex/bounds.html

vic
quelle