2009-08-21 8 views
4

Je suis à la recherche d'un espace correspondant à exression régulier que si les espaces de Thos ne sont pas entre guillemets ("). Par exemple, dansespaces correspondant à Regex, mais pas dans les « cordes »

Mary had "a little lamb" 

il doit correspondre la première un deuxième espace, mais pas les autres.

Je veux diviser la chaîne ne les espaces qui ne sont pas les guillemets doubles, et non les citations.

J'utilise C++ avec la boîte à outils Qt et voulait utiliser QString :: split (QRegExp). QString est très similaire à std :: string et QRegExp sont essentiellement des regex POSIX encapsulés dans une classe. S'il existe une telle regex, la scission serait triviale.

Exemples:

Mary had "a little lamb"  => Mary,had,"a little lamb" 
1" 2 "3      => 1" 2 "3 (no splitting at ") 
abc def="g h i" "j k" = 12 => abc,def="g h i","j k",=,12 

Désolé pour les modifications, j'étais très imprécise quand j'ai posé la question. J'espère que c'est un peu plus clair maintenant.

+0

La question est répondue ici: [En utilisant regex pour remplacer tous les espaces entre guillemets dans Ruby] (http://stackoverflow.com/questions/205521/using-regex-to-replace-all-spaces-not-in -quotes-in-ruby) –

Répondre

7

(Je sais que vous avez posté presque exactement la même réponse vous-même, mais je ne peux pas supporter de juste jeter tout cela. - /)

S'il est possible de résoudre votre problème avec une opération de regex, l'expression régulière devra correspondre à des nombres pairs de guillemets, comme l'a dit MSalters. Cependant, une regex fractionnée ne doit correspondre qu'aux espaces que vous partagez, donc le reste du travail doit être fait dans un lookahead. Voici ce que j'utiliserais:

" +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)" 

Si le texte est bien formé, un pour un même préanalyse nombre de citations est suffisante pour déterminer que l'espace juste adapté est pas à l'intérieur d'une séquence citée. C'est à dire, lookbehinds ne sont pas nécessaires, ce qui est bien parce que QRegExp ne semble pas les supporter. Les citations échappées peuvent être accommodées aussi, mais la regex devient un peu plus grande et plus laide. Mais si vous ne pouvez pas être sûr que le texte est bien formé, il est très peu probable que vous pourrez résoudre votre problème avec split(). Par ailleurs, QRegExp fait et non implémente POSIX regular expressions - s'il le faisait, il ne supporterait pas les lookaheads ni les lookbehinds. Au lieu de cela, il tombe dans la catégorie vaguement définie des arômes regex compatibles Perl.

+0

Eh bien, j'ai écrit _basically_ POSIX ;-) Quand j'ai cherché lookbehinds, j'ai remarqué qu'il y avait quelque chose qui manquait. Mais peu de temps après, j'ai remarqué que je n'en avais même pas besoin, en supposant toujours des citations appariées. Peut-être que je devrais déposer un bug à Qt. Quoi qu'il en soit, +1 pour l'amélioration de l'expression rationnelle. – hirschhornsalz

+1

Je me rends compte que vous utilisiez vaguement le terme "POSIX"; Je faisais simplement remarquer que les autres pourraient ne pas. J'ai dû rechercher QRegExp pour être sûr que * n'était pas * standard POSIX (ou, plus précisément, que je pourrais recommander une solution basée sur lookahead). –

4

Que devrait-il arriver à "a" b "c"?

Notez que dans la sous-chaîne " b " les espaces sont entre guillemets.

- modifier -

Je suppose un espace est « entre guillemets » si elle est précédée et suivie d'un nombre impair de guillemets standards (ie U + 0022, j'ignorerons drôle Unicode « citations ").

Cela signifie que vous avez besoin de l'expression rationnelle suivante: ^[^"]*("[^"]*"[^"]*)*"[^"]* [^"]*"[^"]*("[^"]*"[^"]*)*$

("[^"]*"[^"]*) représente une paire de guillemets. ("[^"]*"[^"]*)* est un montant pair de citations, ("[^"]"[^"]*)*" une quantité impaire. Ensuite, il y a la partie de chaîne entre guillemets, suivie d'un autre nombre impair de guillemets. ^$ Les ancres sont nécessaires car vous devez compter chaque citation du début de la chaîne. Cela répond au problème de sous-chaîne " b " ci-dessus en ne regardant jamais les sous-chaînes. Le prix est que chaque caractère de votre entrée doit correspondre à la chaîne entière, ce qui transforme cette opération en une opération de division O (N * N).

La raison pour laquelle vous pouvez le faire dans une regex est parce qu'il y a une quantité limitée de mémoire nécessaire. Effectivement juste un bit; "ai-je déjà vu un nombre pair ou impair de citations?". Vous n'avez pas à faire correspondre les paires individuelles "".

Ce n'est pas la seule interprétation possible. Si vous incluez “funny Unicode quotes” qui doit être associé, vous devez également traiter les chaînes ““double quoted””. Cela signifie que vous avez besoin d'un nombre de ouvert, ce qui signifie que vous avez besoin d'un stockage infini, ce qui signifie que ce n'est plus un langage régulier, ce qui signifie que vous ne pouvez pas utiliser une regex. QED.

Quoi qu'il en soit, même si c'était possible, vous voudriez toujours un parseur approprié. Le comportement O (N * N) pour compter le nombre de citations précédant chaque caractère n'est pas drôle. Si vous savez déjà qu'il y a X citations précédant Str [N], cela devrait être une opération O (1) pour déterminer combien de citations précèdent Str [N + 1], pas O (N). Les réponses possibles sont après tout juste X ou X + 1!

+0

C'est une question et non une réponse. Utilisez un commentaire – Gumbo

+0

C'est une réponse avec un point d'interrogation;) Le problème est qu'il utilise le mauvais outil (regex au lieu d'un analyseur basé sur la pile) pour son problème.Et il n'y a pas de "raison proche: le problème ne peut pas être résolu avec regex" – MSalters

+0

La raison pour laquelle je demande est parce que je voulais éviter d'utiliser un analyseur. Je voulais la solution "bon marché". S'il n'y a pas de solution utilisant regex, fournissez une preuve mathématique et je l'accepterai comme réponse :-) N'a même pas besoin d'être strict et rigoureux :-) – hirschhornsalz

-2

Solution regex la plus simple: associez des espaces entiers ET des guillemets.Filtrer les citations plus tard

"[^"]*"|\s 
1

Si la citation dans les chaînes est simple (comme vos exemples), vous pouvez utiliser l'alternance. Cette regex cherche d'abord une simple chaîne entre guillemets. à défaut, il trouve des espaces.

/(\"[^\"]*\"| +)/ 

En Perl, si vous utilisez le regroupement dans le regex lors de l'appel split(), la fonction retourne non seulement les éléments, mais aussi les groupes capturés (dans ce cas, notre delimiter). Si vous filtrez ensuite les délimiteurs vides et espaces, vous obtiendrez la liste d'éléments désirée. Je ne sais pas si une stratégie similaire travaillerait en C++, mais le code Perl suivant FONCTIONNE:

use strict; 
use warnings; 
while (<DATA>){ 
    chomp; 
    my @elements = split /(\"[^\"]*\"| +)/, $_; 
    @elements = grep {length and /[^ ]/} @elements; 
    # Do stuff with @elements 
} 

__DATA__ 
Mary had "a little lamb" 
1" 2 "3 
abc def="g h i" "j k" = 12 
4

MSalters m'a poussé sur la bonne voie. Le problème avec sa réponse que la regex qu'il donne correspond toujours à toute la chaîne et est donc inadapté pour split(), mais cela peut être en partie racheté par un match de lookahead. En supposant que les citations sont toujours appariées (elles le sont en effet), je peux diviser sur chaque espace qui est suivi d'un nombre pair de citations.

Le regex sans C échappe et entre guillemets simples ressemble

' (?=[^"]*("[^"]*"[^"]*)*$)' 

Dans la source finalement ressemblait à (en utilisant Qt et C++)

QString buf("Mary had \"a little lamb\""); // string we want to split 
QStringList splitted = buf.split(QRegExp(" (?=[^\"]*(\"[^\"]*\"[^\"]*)*$)")); 

simple, hein?

Pour la performance, les chaînes sont analysées une fois au début du programme, elles sont quelques dizaines et elles sont inférieures à cent caractères. Je vais tester son temps d'exécution avec de longues chaînes, juste pour être sûr que rien ne se passe ;-)

Questions connexes