Regex pour fractionner une chaîne en utilisant un espace lorsqu’elle n’est pas entourée de guillemets simples ou doubles

Je suis nouveau dans les expressions régulières et apprécierais votre aide. J’essaie de rassembler une expression qui divisera la chaîne exemple en utilisant tous les espaces qui ne sont pas entourés de guillemets simples ou doubles. Ma dernière tentative ressemble à ceci: (?!") Et ne fonctionne pas tout à fait. Elle divise l’espace avant la citation.

Exemple d’entrée:

 This is a ssortingng that "will be" highlighted when your 'regular expression' matches something. 

Sortie désirée:

 This is a ssortingng that will be highlighted when your regular expression matches something. 

Notez que "will be" et 'regular expression' conservent l’espace entre les mots.

Je ne comprends pas pourquoi tous les autres proposent des expressions régulières aussi complexes ou un code aussi long. Essentiellement, vous voulez extraire deux types d’éléments de votre chaîne: des séquences de caractères qui ne sont pas des espaces ou des guillemets et des séquences de caractères qui commencent et se terminent par une citation entre deux guillemets. Vous pouvez facilement associer ces choses à cette expression régulière:

 [^\s"']+|"([^"]*)"|'([^']*)' 

J’ai ajouté les groupes de capture car vous ne voulez pas les guillemets dans la liste.

Ce code Java construit la liste, en ajoutant le groupe de capture s’il correspondait pour exclure les guillemets, et en ajoutant la correspondance globale des expressions rationnelles si le groupe de capture ne correspondait pas (un mot non cité était associé).

 List matchList = new ArrayList(); Pattern regex = Pattern.comstack("[^\\s\"']+|\"([^\"]*)\"|'([^']*)'"); Matcher regexMatcher = regex.matcher(subjectSsortingng); while (regexMatcher.find()) { if (regexMatcher.group(1) != null) { // Add double-quoted ssortingng without the quotes matchList.add(regexMatcher.group(1)); } else if (regexMatcher.group(2) != null) { // Add single-quoted ssortingng without the quotes matchList.add(regexMatcher.group(2)); } else { // Add unquoted word matchList.add(regexMatcher.group()); } } 

Si cela ne vous dérange pas d’avoir les guillemets dans la liste renvoyée, vous pouvez utiliser un code beaucoup plus simple:

 List matchList = new ArrayList(); Pattern regex = Pattern.comstack("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); Matcher regexMatcher = regex.matcher(subjectSsortingng); while (regexMatcher.find()) { matchList.add(regexMatcher.group()); } 

Il existe plusieurs questions sur StackOverflow qui couvrent cette même question dans divers contextes en utilisant des expressions régulières. Par exemple:

  • parsings ssortingngs: extraire des mots et des phrases
  • Meilleur moyen d’parsingr le texte séparé par l’espace

UPDATE : exemple de regex pour gérer les chaînes simples et doubles. Ref: Comment puis-je diviser une chaîne sauf entre guillemets?

 m/('.*?'|".*?"|\S+)/g 

Testé avec un extrait rapide de Perl et la sortie était comme reproduite ci-dessous. Fonctionne également pour les chaînes vides ou les chaînes en blanc si elles se trouvent entre guillemets (pas sûr que cela soit souhaité ou non).

 This is a ssortingng that "will be" highlighted when your 'regular expression' matches something. 

Notez que cela inclut les guillemets eux-mêmes dans les valeurs correspondantes, bien que vous puissiez les supprimer avec une chaîne de remplacement ou modifier l’expression rationnelle pour ne pas les inclure. Je vais laisser cela comme un exercice pour le lecteur ou un autre poster pour le moment, car 2h du matin est bien trop tard pour jouer avec les expressions régulières;)

Si vous souhaitez autoriser les guillemets échappés dans la chaîne, vous pouvez utiliser quelque chose comme ceci:

 (?:(['"])(.*?)(?< !\\)(?>\\\\)*\1|([^\s]+)) 

Les chaînes citées seront le groupe 2, les mots simples non classés seront le groupe 3.

Vous pouvez l’essayer sur différentes chaînes ici: http://www.fileformat.info/tool/regex.htm ou http://gskinner.com/RegExr/

La regex de Jan Goyvaerts est la meilleure solution que j’ai trouvée jusqu’ici, mais crée également des correspondances vides (nulles), qu’il exclut dans son programme. Ces correspondances vides apparaissent également des testeurs de regex (par exemple, rubular.com). Si vous tournez les recherches (cherchez d’abord les parties entre guillemets et les mots séparés par des espaces), vous pouvez le faire une fois avec:

 ("[^"]*"|'[^']*'|[\S]+)+ 
 (?< !\G".{0,99999})\s|(?<=\G".{0,99999}")\s 

Cela correspondra aux espaces non entourés de guillemets doubles. Je dois utiliser min, max {0,99999} car Java ne supporte pas * et + in lookbehind.

Il sera probablement plus facile de rechercher la chaîne en saisissant chaque partie, plutôt que de la diviser.

La raison étant, vous pouvez la faire diviser dans les espaces avant et après "will be" . Mais, je ne peux penser à aucun moyen de spécifier l’ignorance de l’espace entre les divisions.

(pas Java réel)

 ssortingng = "This is a ssortingng that \"will be\" highlighted when your 'regular expression' matches something."; regex = "\"(\\\"|(?!\\\").)+\"|[^ ]+"; // search for a quoted or non-spaced group final = new Array(); while (ssortingng.length > 0) { ssortingng = ssortingng.sortingm(); if (Regex(regex).test(ssortingng)) { final.push(Regex(regex).match(ssortingng)[0]); ssortingng = ssortingng.replace(regex, ""); // progress to next "word" } } 

En outre, la capture de citations individuelles peut entraîner des problèmes:

 "Foo's Bar 'n Grill" //=> "Foo" "s Bar " "n" "Grill" 

Ssortingng.split() n’est pas utile ici car il n’y a aucun moyen de faire la distinction entre les espaces entre guillemets (ne pas diviser) et ceux à l’extérieur (scinder). Matcher.lookingAt() est probablement ce dont vous avez besoin:

 Ssortingng str = "This is a ssortingng that \"will be\" highlighted when your 'regular expression' matches something."; str = str + " "; // add trailing space int len = str.length(); Matcher m = Pattern.comstack("((\"[^\"]+?\")|('[^']+?')|([^\\s]+?))\\s++").matcher(str); for (int i = 0; i < len; i++) { m.region(i, len); if (m.lookingAt()) { String s = m.group(1); if ((s.startsWith("\"") && s.endsWith("\"")) || (s.startsWith("'") && s.endsWith("'"))) { s = s.substring(1, s.length() - 1); } System.out.println(i + ": \"" + s + "\""); i += (m.group(0).length() - 1); } } 

qui produit la sortie suivante:

 0: "This" 5: "is" 8: "a" 10: "ssortingng" 17: "that" 22: "will be" 32: "highlighted" 44: "when" 49: "your" 54: "regular expression" 75: "matches" 83: "something." 

J’ai aimé l’approche de Marcus, cependant, je l’ai modifiée pour autoriser le texte à proximité des guillemets et pour supporter les caractères “et”. Par exemple, j’ai eu besoin d’une valeur = pour ne pas la diviser en [a = ” une certaine valeur “].

 (?< !\\G\\S{0,99999}[\"'].{0,99999})\\s|(?<=\\G\\S{0,99999}\".{0,99999}\"\\S{0,99999})\\s|(?<=\\G\\S{0,99999}'.{0,99999}'\\S{0,99999})\\s" 

Un couple, espérons-le, des réglages utiles sur la réponse acceptée de Jan:

 (['"])((?:\\\1|.)+?)\1|([^\s"']+) 
  • Permet des guillemets échappés dans les chaînes entre guillemets
  • Évite de répéter le motif pour les guillemets simples et doubles; Cela simplifie également l’ajout de symboles supplémentaires si nécessaire (au désortingment d’un groupe de capture supplémentaire)

L’approche de Jan est excellente mais en voici une autre pour le disque.

Si vous voulez réellement diviser comme mentionné dans le titre, en gardant les guillemets dans "will be" et 'regular expression' , vous pouvez utiliser cette méthode qui est directement issue de Match (ou remplacer) un modèle sauf dans les situations s1, s2 , s3 etc

La regex:

 '[^']*'|\"[^\"]*\"|( ) 

Les deux alternances de gauche correspondent à des 'quoted ssortingngs' complètes et à des "double-quoted ssortingngs" . Nous allons ignorer ces correspondances. Le côté droit correspond aux espaces du groupe 1 et les capture, et nous soaps que ce sont les bons espaces car ils n’ont pas été mis en correspondance avec les expressions de gauche. Nous remplaçons ceux avec SplitHere puis divisés sur SplitHere . Encore une fois, ceci est pour un cas réel où vous voulez "will be" , ce ne will be pas will be .

Voici une implémentation complète (voir les résultats sur la démo en ligne ).

 import java.util.*; import java.io.*; import java.util.regex.*; import java.util.List; class Program { public static void main (Ssortingng[] args) throws java.lang.Exception { Ssortingng subject = "This is a ssortingng that \"will be\" highlighted when your 'regular expression' matches something."; Pattern regex = Pattern.comstack("\'[^']*'|\"[^\"]*\"|( )"); Matcher m = regex.matcher(subject); SsortingngBuffer b= new SsortingngBuffer(); while (m.find()) { if(m.group(1) != null) m.appendReplacement(b, "SplitHere"); else m.appendReplacement(b, m.group(0)); } m.appendTail(b); Ssortingng replaced = b.toSsortingng(); Ssortingng[] splits = replaced.split("SplitHere"); for (Ssortingng split : splits) System.out.println(split); } // end main } // end Program 

Je suis raisonnablement certain que cela n’est pas possible en utilisant uniquement des expressions régulières. Vérifier si quelque chose est contenu dans une autre balise est une opération d’parsing. Cela semble être le même problème que si vous essayiez d’parsingr XML avec une regex – cela ne peut pas être fait correctement. Vous pourrez peut-être obtenir le résultat souhaité en appliquant une expression régulière non gourmande et non globale qui correspond aux chaînes entre guillemets, puis, une fois que vous ne pourrez plus rien trouver, divisez-la en plusieurs espaces. problèmes, y compris le suivi de l’ordre d’origine de toutes les sous-chaînes. Votre meilleur pari est de simplement écrire une fonction très simple qui parcourt la chaîne et extrait les jetons de votre choix.

Vous pouvez également essayer ceci:

  Ssortingng str = "This is a ssortingng that \"will be\" highlighted when your 'regular expression' matches something"; Ssortingng ss[] = str.split("\"|\'"); for (int i = 0; i < ss.length; i++) { if ((i % 2) == 0) {//even String[] part1 = ss[i].split(" "); for (String pp1 : part1) { System.out.println("" + pp1); } } else {//odd System.out.println("" + ss[i]); } } 

Si vous utilisez c #, vous pouvez utiliser

 ssortingng input= "This is a ssortingng that \"will be\" highlighted when your 'regular expression' matches "; List list1 = Regex.Matches(input, @"(?\w+)|\""(?[\w\s]*)""|'(?[\w\s]*)'|< (?[\w\s]*)>").Cast().Select(m => m.Groups["match"].Value).ToList(); foreach(var v in list1) Console.WriteLine(v); 

J’ai spécifiquement ajouté ” | < (? [\ W \ s] *)> ” pour mettre en évidence que vous pouvez spécifier n’importe quel caractère pour grouper des phrases. (Dans ce cas, j’utilise <> pour grouper.

Le résultat est:

 This is a ssortingng that will be highlighted when your regular expression matches something random