44 votes

Recherche de chaînes entre guillemets avec des guillemets échappés en C # à l'aide d'une expression régulière

J'essaie de trouver toutes les cités de texte sur une seule ligne.

Exemple:

"Some Text"
"Some more Text"
"Even more text about \"this text\""

J'ai besoin d'obtenir:

  • "Some Text"
  • "Some more Text"
  • "Even more text about \"this text\""

\"[^\"\r]*\" me donne tout sauf la dernière qui, en raison de l'échappé des citations.

J'ai lu sur \"[^\"\\]*(?:\\.[^\"\\]*)*\" de travail, mais j'obtiens une erreur au moment de l'exécution:

parsing ""[^"\]*(?:\.[^"\]*)*"" - Unterminated [] set.

Comment puis-je résoudre ce problème?

83voto

Alan Moore Points 39365

Ce que vous avez, il est un exemple de Friedl est "déroulé de la boucle" technique", mais vous semblez avoir une certaine confusion sur la façon de l'exprimer comme une chaîne littérale. Voici à quoi il devrait ressembler à la regex compilateur:

"[^"\\]*(?:\\.[^"\\]*)*"

La première "[^"\\]* correspond à un guillemet suivi par zéro ou plus de tous les caractères autres que des guillemets ou des barres obliques inverses. La partie à lui seul, le long avec le dernier ", correspondra à une simple chaîne de caractères entre guillemets avec pas intégré les séquences d'échappement, comme "this" ou "".

Si elle ne rencontre une barre oblique inverse, \\. consomme de la barre oblique inverse et tout ce qui suit, et [^"\\]* (encore une fois) consomme tout jusqu'à la prochaine barre oblique ou un guillemet simple. La partie répété autant de fois que nécessaire jusqu'à ce qu'un sans échappement guillemet tourne vers le haut (ou il atteint la fin de la chaîne et le match tentative échoue).

Notez que ce match "foo\"- en \"foo\"-"bar". Cela peut sembler pour exposer une faille dans la regex, mais il n'en est rien; c'est l' entrée qui est invalide. L'objectif était de faire correspondre une chaîne entre guillemets, éventuellement contenant des anti-slash-échappé citations, incorporé dans un autre texte--pourquoi y aurait-il échappé citations à l'extérieur de la cité de cordes? Si vous avez vraiment besoin de soutien que vous avez beaucoup plus d'un problème complexe, nécessitant une approche très différente.

Comme je l'ai dit, le ci-dessus est de savoir comment les regex devrait se tourner vers les regex compilateur. Mais vous l'écrivez dans la forme d'une chaîne littérale, et ceux qui ont tendance à traiter certains caractères spécialement--c'est à dire, les barres obliques inverses et les guillemets. Heureusement, C#verbatim les chaînes de vous épargner les tracas d'avoir à double-barres obliques inverses; vous avez juste à échapper à chaque guillemet avec un autre guillemet:

Regex r = new Regex(@"""[^""\\]*(?:\\.[^""\\]*)*""");

La règle est donc double guillemets pour le compilateur C# et double anti-slash pour les regex compilateur--agréable et facile. Cette regex peut sembler un peu maladroit, avec les trois guillemets à chaque extrémité, mais considère l'alternative:

Regex r = new Regex("\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*\"");

En Java, vous toujours avoir à écrire de cette façon. :-( D'ailleurs, si vous souhaitez vous assurer qu'il n'y a pas de ligne de séparation des caractères dans les chaînes entre guillemets, vous pouvez les inclure dans les classes de caractère niées:

Regex r = new Regex(@"""[^""\r\n\\]*(?:\\.[^""\r\n\\]*)*""");

Le point en \\. déjà exclut les séparateurs de ligne, tant que vous ne spécifiez pas l' Singleline option.

12voto

Ricardo Nolde Points 5630

Regex pour saisir des chaînes de caractères (avec \ pour le personnage de s'échapper), pour le .NETTE du moteur:

(?>(?(STR)(?(ESC).(?<-ESC>)|\\(?<ESC>))|(?!))|(?(STR)"(?<-STR>)|"(?<STR>))|(?(STR).|(?!)))+   

Ici, une "amie" de la version:

(?>                            | especify nonbacktracking
   (?(STR)                     | if (STRING MODE) then
         (?(ESC)               |     if (ESCAPE MODE) then
               .(?<-ESC>)      |          match any char and exits escape mode (pop ESC)
               |               |     else
               \\(?<ESC>)      |          match '\' and enters escape mode (push ESC)
         )                     |     endif
         |                     | else
         (?!)                  |     do nothing (NOP)
   )                           | endif
   |                           | -- OR
   (?(STR)                     | if (STRING MODE) then
         "(?<-STR>)            |     match '"' and exits string mode (pop STR)
         |                     | else
         "(?<STR>)             |     match '"' and enters string mode (push STR)
   )                           | endif
   |                           | -- OR
   (?(STR)                     | if (STRING MODE) then
         .                     |     matches any character
         |                     | else
         (?!)                  |     do nothing (NOP)  
   )                           | endif
)+                             | REPEATS FOR EVERY CHARACTER

Basé sur http://tomkaminski.com/conditional-constructs-net-regular-expressions des exemples. Il s'appuie, dans des citations d'équilibrage. Je l'utilise avec beaucoup de succès. L'utiliser avec d' Singleline drapeau.

Jouer avec les regexes, je recommande Rad Logiciel Expression Régulière Designer, qui a une belle "Éléments de Langage" de l'onglet avec un accès rapide à certaines des instructions de base. Il est basé sur .NET moteur d'expressions régulières.

4voto

Tim Pietzcker Points 146308
"(\\"|\\\\|[^"\\])*"

devrait fonctionner. Match soit une fuite de devis, un échappé de la barre oblique inverse, ou tout autre caractère sauf un devis ou un caractère barre oblique inverse. Répétez.

En C#:

StringCollection resultList = new StringCollection();
Regex regexObj = new Regex(@"""(\\""|\\\\|[^""\\])*""");
Match matchResult = regexObj.Match(subjectString);
while (matchResult.Success) {
    resultList.Add(matchResult.Value);
    matchResult = matchResult.NextMatch();
} 

Edit: Ajouté échappé à la barre oblique inverse à la liste pour gérer correctement l' "This is a test\\".

Explication:

D'abord correspondre à une citation de caractères.

Ensuite, les alternatives sont évaluées de gauche à droite. Le moteur premier essaie de faire correspondre une fuite de devis. Si cela ne correspond pas, il tente une échappée barre oblique inverse. De cette façon, il peut distinguer "Hello \" string continues" et "String ends here \\".

Si l'un ou l'autre n'est pas le cas, alors tout le reste est permis, sauf pour un devis ou un caractère barre oblique inverse. Puis répétez.

Enfin, correspond au cours de clôture.

3voto

Blankasaurus Points 4053

Je recommande d'obtenir RegexBuddy . Il vous permet de jouer avec jusqu'à ce que vous vous assuriez que tout dans votre ensemble de tests correspond.

Quant à votre problème, j'essaierais quatre / au lieu de deux:

 \"[^\"\\\\]*(?:\\.[^\"\\\\]*)*\"
 

2voto

Kamarey Points 4416

L'expression régulière

 (?<!\\)".*?(?<!\\)"
 

gérera également le texte commençant par une citation échappée:

 \"Some Text\" Some Text "Some Text", and "Some more Text" an""d "Even more text about \"this text\""
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X