web-dev-qa-db-fra.com

Regex pour choisir des virgules en dehors des citations

Je ne suis pas tout à fait sûr si cela est possible, alors je me tourne vers vous.

J'aimerais trouver une regex qui choisira toutes les virgules qui tombent en dehors des jeux de devis.

Par exemple:

'foo' => 'bar',
'foofoo' => 'bar,bar'

Cela choisirait la seule virgule sur la ligne 1, après 'bar',

Je ne me soucie pas vraiment de citations doubles Simple VS.

Quelqu'un a-t-il eu des pensées? Je me sens comme ça devrait être possible avec des Readaheads, mais mon regex fu est trop faible.

41
SocialCensus

Cela correspondra à n'importe quelle chaîne jusqu'à et incluant la première non citée ",". Est-ce ce que vous voulez?

/^([^"]|"[^"]*")*?(,)/

Si vous voulez tous (et comme un contre-exemple au gars qui a dit que ce n'était pas possible), vous pouvez écrire:

/(,)(?=(?:[^"]|"[^"]*")*$)/

qui vont tous correspondre tous. Ainsi

'test, a "comma,", bob, ",sam,",here'.gsub(/(,)(?=(?:[^"]|"[^"]*")*$)/,';')

remplace toutes les virgules non Intérieurs de citations avec des points-virgules et produit:

'test; a "comma,"; bob; ",sam,";here'

Si vous en avez besoin pour travailler sur des pauses de ligne, ajoutez simplement le drapeau M (multilignes).

88
MarkusQ

Les regexnes ci-dessous correspondraient à toutes les virgules qui sont présentes en dehors des citations doubles,

,(?=(?:[^"]*"[^"]*")*[^"]*$)

DÉMO

[~ # ~] ou [~ # ~ ~] (pcre uniquement)

"[^"]*"(*SKIP)(*F)|,

"[^"]*" correspond à tout le double bloc cité. C'est-à-dire dans cette buz,"bar,foo" Entrée, cette regex correspondrait à "bar,foo" seul. Maintenant ce qui suit (*SKIP)(*F) rend le match à échouer. Ensuite, il passe au motif qui était à côté de | Symbole et essaie de faire correspondre les caractères de la chaîne restante. C'est-à-dire dans notre sortie , à côté du modèle | ne correspondra que la virgule qui était juste après l'buz. Notez que cela ne correspond pas à la virgule présentée dans des guillemets doubles, car nous faisons déjà la partie citée à sauter.

DÉMO


La regex ci-dessous correspondrait à toutes les virgules qui sont présentes dans les citations doubles,

,(?!(?:[^"]*"[^"]*")*[^"]*$)

DÉMO

15
Avinash Raj

Bien qu'il soit possible de pirater une regex (et j'apprécie des regregs autant que le prochain gars), vous aurez des problèmes plus tôt ou plus tard en essayant de gérer des sous-chaînes sans analyseur plus avancé. Les moyens possibles d'avoir des problèmes incluent des citations mixtes et des citations échappées.

Cette fonction divisera une chaîne sur les virgules, mais pas les virgules situées dans une chaîne à une seule ou double citée. Il peut être facilement étendu avec des caractères supplémentaires à utiliser comme citations (bien que des paires de caractères comme "" auraient besoin de quelques lignes de code supplémentaires) et vous vous diront même si vous avez oublié de fermer une citation dans vos données:

function splitNotStrings(str){
  var parse=[], inString=false, escape=0, end=0

  for(var i=0, c; c=str[i]; i++){ // looping over the characters in str
    if(c==='\\'){ escape^=1; continue} // 1 when odd number of consecutive \
    if(c===','){
      if(!inString){
        parse.Push(str.slice(end, i))
        end=i+1
      }
    }
    else if(splitNotStrings.quotes.indexOf(c)>-1 && !escape){
      if(c===inString) inString=false
      else if(!inString) inString=c
    }
    escape=0
  }
  // now we finished parsing, strings should be closed
  if(inString) throw SyntaxError('expected matching '+inString)
  if(end<i) parse.Push(str.slice(end, i))
  return parse
}

splitNotStrings.quotes="'\"" // add other (symmetrical) quotes here
2
Touffy

Essayez cette expression régulière:

(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*=>\s*(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*,

Cela permet également des chaînes comme "'foo\'bar' => 'bar\\', ".

1
Gumbo

@Socialcensus, l'exemple que vous avez donné dans le commentaire à MarkusQ, où vous jetez 'à côté de la ", ne fonctionne pas avec l'exemple Markusq donna juste au-dessus que si nous change Sam à Sam's: (test, une "virgule,", Bob ", Sam's,", ici) n'a pas de match contre (,) (? = (?: ["| ']] [^ "'] ") $). En fait, le problème lui-même "Je ne me soucie pas vraiment de célibataires vs doubles citations", est ambiguë. Vous devez être clair ce que vous entendez en cité avec "ou avec". Par exemple, la nidification est autorisée ou non? Si oui, combien de niveaux? Si seulement 1 niveau imbriqué, qu'advient-il d'une virgule en dehors de la citation intérieure imbriquée, mais à l'intérieur de la citation de nidification extérieure? Vous devez également considérer que les citations simples se produisent par eux-mêmes comme des apostrophes (c'est-à-dire comme le contre-exemple que j'ai donné plus tôt avec Sam). Enfin, la regex que vous avez faite ne traite pas vraiment des guillemets simples au pair avec des guillemets, car il suppose que le dernier type de guillemet est nécessairement une double citation - et remplaçant ce dernier double devis avec ['| "] a également un problème Si le texte ne vient pas avec une citation correcte (ou si des apostrophes sont utilisés), je suppose que nous pourrions probablement supposer que toutes les citations sont correctement délimitées.

La REGEXP de Markusq répond à la question: trouver toutes les virgules qui ont un nombre pare-ci-dessus après cela (c'est-à-dire des citations doubles extérieures) et ne tiennent pas compte de toutes les virgules qui ont un nombre impair de guillemets à deux cotes (c'est-à-dire des citations doubles). C'est généralement la même solution que ce que vous voulez probablement, mais regardons quelques anomalies. Premièrement, si quelqu'un quitte une guilleme à la fin, cette REGEXP trouve toutes les mauvaises virgules plutôt que de trouver les personnes désirées ou de ne pas correspondre à celle-ci. Bien sûr, si une double citation est manquante, tous les paris sont éteints car il pourrait ne pas être clair si le manquant appartient à la fin ou appartient au début; Cependant, il existe un cas qui est légitime et où la regex pourrait éventuellement échouer (c'est la deuxième "anomalie"). Si vous ajustez le REGEXP pour passer à travers les lignes de texte, vous devez savoir que citer plusieurs paragraphes consécutifs nécessite une seule double citation au début de chaque paragraphe et laissez la citation à la fin de chaque paragraphe, à l'exception de fin du dernier paragraphe. Cela signifie que sur l'espace de ces paragraphes, la regex échouera à certains endroits et réussira dans d'autres.

Des exemples et de brèves discussions sur la citation de paragraphe et la citation imbriquée peuvent être trouvées ici http://fr.wikipedia.org/wiki/quotation_mark .

1
Jose_X

La réponse de Markusq a été excellente pour moi pendant environ un an, jusqu'à ce qu'il ne soit pas. Je viens de recevoir une erreur de dépassement de pile sur une ligne avec environ 120 virgules et 3682 caractères au total. En Java, comme ceci:

        String[] cells = line.split("[\t,](?=(?:[^\"]|\"[^\"]*\")*$)", -1);

Voici mon remplacement extrêmement inélégant qui ne pile pas débordement:

private String[] extractCellsFromLine(String line) {
    List<String> cellList = new ArrayList<String>();
    while (true) {
        String[] firstCellAndRest;
        if (line.startsWith("\"")) {
            firstCellAndRest = line.split("([\t,])(?=(?:[^\"]|\"[^\"]*\")*$)", 2);
        }
        else {
            firstCellAndRest = line.split("[\t,]", 2);                
        }
        cellList.add(firstCellAndRest[0]);
        if (firstCellAndRest.length == 1) {
            break;
        }
        line = firstCellAndRest[1];
    }
    return cellList.toArray(new String[cellList.size()]);
}
1
sullivan-