Qu'est-ce qu'une limite de mot dans regex?

137

J'utilise des expressions régulières Java dans Java 1.6 (pour analyser la sortie numérique, entre autres) et je ne trouve pas de définition précise de \b("word boundary"). J'avais supposé que ce -12serait un "mot entier" (correspondant à \b\-?\d+\b) mais il semble que cela ne fonctionne pas. Je serais reconnaissant de connaître les moyens de faire correspondre les nombres séparés par des espaces.

Exemple:

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

Cela renvoie:

true
false
true
peter.murray.rust
la source
Pouvez-vous poster un petit exemple avec l'entrée et la sortie attendue?
Brent écrit le code
Exemple de motif pattern = Pattern.compile ("\\ s * \\ b \\ -? \\ d + \\ s *"); Chaîne plus = "12"; System.out.println ("" + pattern.matcher (plus) .matches ()); Chaîne moins = "-12"; System.out.println ("" + pattern.matcher (moins) .matches ()); pattern = Pattern.compile ("\\ s * \\ -? \\ d + \\ s *"); System.out.println ("" + pattern.matcher (moins) .matches ()); donne: true false true
peter.murray.rust

Réponses:

97

Une limite de mot, dans la plupart des dialectes regex, est une position entre \wet \W(caractère non-mot), ou au début ou à la fin d'une chaîne si elle commence ou se termine (respectivement) par un caractère de mot ( [0-9A-Za-z_]).

Ainsi, dans la chaîne "-12", il correspondrait avant le 1 ou après le 2. Le tiret n'est pas un caractère de mot.

Brianary
la source
35
Correctamundo. \best une assertion de largeur nulle qui correspond s'il y \wen a d'un côté et qu'il y \Wen a de l'autre ou que la position est le début ou la fin de la chaîne. \west arbitrairement défini comme étant des caractères "identificateurs" (alnums et traits de soulignement), pas comme quelque chose de particulièrement utile pour l'anglais.
hobbs
100% correct. Toutes mes excuses pour ne pas seulement avoir commenté le vôtre. J'ai appuyé sur Soumettre avant de voir votre réponse.
Brent écrit le code
5
par souci de compréhension, est-il possible de réécrire l'expression régulière \bhello\bsans utiliser \b(en utilisant \w, \Wet autres)?
David Portabella
5
Sorte de :, (^|\W)hello($|\W)sauf qu'il ne capturerait aucun caractère non-mot avant et après, donc ce serait plutôt (^|(?<=\W))hello($|(?=\W))(en utilisant des assertions lookahead / lookbehind).
brianary le
6
@brianary un peu plus simple: (?<!\w)hello(?!\w).
David Knipe
28

Une limite de mot peut apparaître dans l'une des trois positions suivantes:

  1. Avant le premier caractère de la chaîne, si le premier caractère est un caractère de mot.
  2. Après le dernier caractère de la chaîne, si le dernier caractère est un caractère de mot.
  3. Entre deux caractères dans la chaîne, où l'un est un caractère de mot et l'autre n'est pas un caractère de mot.

Les caractères des mots sont alphanumériques; un signe moins ne l'est pas. Pris à partir de didacticiel Regex .

Homme-loupDragon
la source
21

Au cours de l'apprentissage de l'expression régulière, j'étais vraiment coincé dans le métacaractère qui est \b. Je n'ai en effet pas compris sa signification en me demandant « ce que c'est, ce que c'est » de façon répétitive. Après quelques tentatives d'utilisation du site Web , je fais attention aux tirets verticaux roses à chaque début et à la fin des mots. Je l'ai bien compris à ce moment-là. C'est maintenant exactement la limite du mot ( \w) .

Mon point de vue est simplement axé sur la compréhension immensément. La logique derrière cela devrait être examinée à partir d'une autre réponse.

entrez la description de l'image ici

snr
la source
3
Un très bon site pour comprendre ce qu'est une limite de mot et comment se déroulent les matchs
vsingh
2
Ce message mérite d'être présenté au lieu de raconter. Une image vaut mille mots.
M_M le
13

Une limite de mot est une position qui est soit précédée d'un caractère de mot et non suivie d'un caractère, soit suivie d'un caractère de mot et non précédée d'un seul.

Alan Moore
la source
8

Je parle de ce que les \blimites de regex -style sont réellement ici .

En bref, ils sont conditionnels . Leur comportement dépend de ce qu'ils côtoient.

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

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

Parfois, ce n'est pas ce que vous voulez. Voir mon autre réponse pour élaboration.

tchrist
la source
8

Je voudrais expliquer la réponse d' Alan Moore

Une limite de mot est une position qui est soit précédée d'un caractère de mot et non suivie d'un caractère, soit suivie d'un caractère de mot et non précédée d'un seul.

Supposons que j'ai une chaîne "Ceci est un c a t, et elle est un génial", et je suis censé remplacer toutes les occurrences par la lettre 'a' uniquement si cette lettre existe à la "limite d'un mot" , ie la lettre aà l'intérieur de «chat» ne doit pas être remplacée.

Je vais donc exécuter regex (en Python ) comme

re.sub("\ba","e", myString.strip())// remplacer apare

de sorte que la sortie sera Ceci est ec un t ee elle de ewesome

Daksh Gargas
la source
5

Je suis tombé sur un problème encore pire lorsque le texte recherche des mots comme .NET, C++, C#et C. On pourrait penser que les programmeurs informatiques sauraient mieux que de nommer un langage pour lequel il est difficile d'écrire des expressions régulières.

Quoi qu'il en soit, c'est ce que j'ai découvert (résumé principalement de http://www.regular-expressions.info , qui est un excellent site): Dans la plupart des versions de regex, les caractères qui correspondent à la classe de caractères abrégés \wsont les les caractères qui sont traités comme des caractères de mots par des limites de mots. Java est une exception. Java prend en charge Unicode pour \bmais pas pour \w. (Je suis sûr qu'il y avait une bonne raison à cela à l'époque).

Le \wsignifie «caractère de mot». Il correspond toujours aux caractères ASCII [A-Za-z0-9_]. Notez l'inclusion du trait de soulignement et des chiffres (mais pas du tiret!). Dans la plupart des versions qui prennent en charge Unicode, \winclut de nombreux caractères d'autres scripts. Il y a beaucoup d'incohérences sur les caractères réellement inclus. Les lettres et les chiffres des scripts alphabétiques et des idéogrammes sont généralement inclus. La ponctuation du connecteur autre que le trait de soulignement et les symboles numériques qui ne sont pas des chiffres peuvent ou non être inclus. XML Schema et XPath incluent même tous les symboles dans \w. Mais Java, JavaScript et PCRE ne correspondent qu'aux caractères ASCII avec\w .

C'est pourquoi les regex basées sur Java recherchent C++, C#ou .NET(même si vous vous souvenez d'échapper au point et aux avantages) sont vissées par le\b .

Remarque: je ne sais pas quoi faire en cas d'erreurs de texte, comme lorsque quelqu'un ne met pas d'espace après un point à la fin d'une phrase. Je l'ai permis, mais je ne suis pas sûr que ce soit nécessairement la bonne chose à faire.

Quoi qu'il en soit, en Java, si vous recherchez du texte pour ces langages aux noms étranges, vous devez remplacer le \bpar des indicateurs d'espaces et de ponctuation avant et après. Par exemple:

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

Puis dans votre test ou fonction principale:

    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 Mes remerciements à http://regexpal.com/ sans qui le monde des regex serait très misérable!

Tihamer
la source
J'ai eu du mal à comprendre pourquoi je ne pouvais pas correspondre, C#mais maintenant c'est plus clair
Mugoma
4

Consultez la documentation sur les conditions aux limites:

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

Découvrez cet exemple:

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

Lorsque vous l'imprimez, notez que la sortie est la suivante:

[J'ai trouvé la valeur -, dans ma chaîne.]

Cela signifie que le caractère "-" n'est pas sélectionné comme étant à la limite d'un mot car il n'est pas considéré comme un caractère de mot. On dirait que @brianary m'a un peu battu, alors il obtient un vote positif.

Brent écrit le code
la source
2

La limite de mot \ b est utilisée où un mot doit être un caractère de mot et un autre un caractère non-mot. L'expression régulière pour un nombre négatif doit être

--?\b\d+\b

vérifier le fonctionnement DEMO

Anubhav Shakya
la source
1

Je crois que votre problème est dû au fait que ce -n'est pas un caractère de mot. Ainsi, la limite du mot correspondra après le- , et ne la capturera donc pas. Les limites de mot correspondent avant le premier et après le dernier caractère de mot dans une chaîne, ainsi que tout endroit où avant il se trouve un caractère de mot ou un caractère non-mot, et après c'est le contraire. Notez également que la limite de mot est une correspondance de largeur nulle.

Une alternative possible est

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

Cela correspondra à tous les nombres commençant par un caractère espace et un tiret facultatif, et se terminant à une limite de mot. Il correspondra également à un nombre commençant au début de la chaîne.

Sean
la source
0

Je pense que c'est la limite (c'est-à-dire le caractère suivant) de la dernière correspondance ou le début ou la fin de la chaîne.


la source
1
Vous pensez à \G: correspond au début de la chaîne (comme \A) lors de la première tentative de correspondance; après cela, il correspond à la position où le match précédent s'est terminé.
Alan Moore
0

lorsque vous utilisez \\b(\\w+)+\\bcela signifie une correspondance exacte avec un mot contenant uniquement des caractères de mot([a-zA-Z0-9])

dans votre cas, par exemple, définir \\bau début de l'expression régulière acceptera -12(avec espace) mais encore une fois, il n'acceptera pas -12(sans espace)

pour référence à l'appui de mes propos: https://docs.oracle.com/javase/tutorial/essential/regex/bounds.html

vic
la source