Question

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

J'aimerais trouver une regex qui sélectionne toutes les virgules en dehors des ensembles de guillemets.

Par exemple:

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

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

Je ne me soucie pas vraiment des guillemets simples vs doubles.

Quelqu'un a-t-il des idées? Je pense que cela devrait être possible avec des readaheads, mais mon regex fu est trop faible.

Était-ce utile?

La solution

Ceci correspondra à toute chaîne allant jusqu'au premier ",", non cité, compris. Est-ce ce que vous voulez?

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

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

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

qui correspondra à tous. Ainsi

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

remplace toutes les virgules et non entre guillemets par des points-virgules et produit:

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

Si vous avez besoin que cela fonctionne sur plusieurs sauts de ligne, ajoutez simplement l'indicateur m (multiligne).

Autres conseils

Les expressions rationnelles ci-dessous correspondent à toutes les virgules présentes en dehors des guillemets,

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

DEMO

OU (PCRE uniquement)

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

"[^"] * * correspond à tout le bloc entre guillemets doubles. C'est-à-dire que dans cette entrée buz, "bar, foo" , cette expression rationnelle correspond uniquement à "bar, foo" . Maintenant, le (* SKIP) (* F) suivant fait échouer la correspondance. Ensuite, il passe au motif qui était à côté du symbole | et tente de faire correspondre les caractères de la chaîne restante. En d’autres termes, dans notre sortie , à côté du motif | correspond uniquement à la virgule placée juste après buz . Notez que cela ne correspond pas à la virgule qui était présente entre guillemets, car nous faisons déjà sauter la partie entre guillemets.

DEMO

La regex ci-dessous correspond à toutes les virgules présentes entre guillemets,

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

DEMO

Bien qu'il soit possible de le pirater avec une regex (et que j'aime bien abuser des regex autant que le gars suivant), vous aurez des ennuis tôt ou tard en essayant de gérer des sous-chaînes sans un analyseur plus avancé. Parmi les solutions possibles pour résoudre les problèmes, citons les citations mélangées et les citations échappées.

Cette fonction divisera une chaîne en virgules, mais pas les virgules contenues dans une chaîne à une ou deux guillemets. Il peut être facilement étendu avec des caractères supplémentaires à utiliser comme guillemets (bien que des paires de caractères comme «» aurait besoin de quelques lignes de code supplémentaires) et vous dira même si vous avez oublié de fermer un devis 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

Essayez cette expression régulière:

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

Ceci autorise également les chaînes telles que “ 'foo \' bar '= > 'bar \\', ”.

La réponse de MarkusQ a bien fonctionné pour moi pendant environ un an, jusqu'à ce que cela ne fonctionne pas. Je viens de recevoir une erreur de débordement de pile sur une ligne contenant environ 120 virgules et 3682 caractères au total. En Java, comme ceci:

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

Voici mon remplaçant extrêmement inélégant qui ne se superpose pas:

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

@SocialCensus, l'exemple que vous avez donné dans le commentaire à MarkusQ, dans lequel vous ajoutez «à côté du», ne fonctionne pas avec l'exemple que MarkusQ a donné ci-dessus, si nous changeons sam en sam's : (test, une "virgule", "bob", "sam,", ici) n'a pas de correspondance avec (,) (? = (?: [^ " ] | ["| '] [^" "] ") $). En fait, le problème lui-même, "je ne me soucie pas vraiment des guillemets simples vs doubles", est ambigu. Vous devez bien comprendre ce que vous voulez dire en citant soit avec " ou avec '. Par exemple, la nidification est-elle 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 imbriquée interne mais à l'intérieur de la citation imbriquée externe? Vous devriez également considérer que les guillemets simples se produisent d'eux-mêmes en tant qu'apostrophes (comme le contre-exemple que j'ai donné précédemment avec sam). Enfin, la regex que vous avez faite ne traite pas vraiment les guillemets simples au pair avec les guillemets doubles car elle suppose que le dernier type de guillemets est nécessairement une guillemet double - et remplacer cette dernière guillemet par ['| "] a aussi un problème si le texte n’est pas accompagné de guillemets corrects (ou si des apostrophes sont utilisés), cependant, je suppose que nous pourrions probablement supposer que tous les guillemets sont correctement délimités.

L'expression rationnelle de MarkusQ répond à la question: trouvez toutes les virgules précédées d'un nombre pair de guillemets (c.-à-d. situées en dehors des guillemets) et ignore toutes les virgules précédées d'un nombre impair de guillemets doubles (c.-à-d. qui se trouvent entre citations). C'est généralement la même solution que ce que vous voulez probablement, mais examinons quelques anomalies. Premièrement, si quelqu'un oublie un guillemet à la fin, cette expression rationnelle trouve toutes les virgules erronées au lieu de rechercher celles qui sont souhaitées ou de ne pas les faire correspondre. Bien sûr, si un guillemet double est manquant, tous les paris sont désactivés puisqu'il n'est peut-être pas clair si celui manquant appartient à la fin ou au début; Cependant, il existe un cas légitime et où la regex pourrait éventuellement échouer (c'est la deuxième "anomalie"). Si vous ajustez l’expression rationnelle de manière à ce qu’elle recouvre les lignes de texte, sachez que pour citer plusieurs paragraphes consécutifs, vous devez placer une simple guillemet double au début de chaque paragraphe et omettre la citation à la fin de chaque paragraphe, à l’exception de la fin du tout dernier paragraphe. Cela signifie que sur l'espace de ces paragraphes, la regex échouera à certains endroits et réussira à d'autres.

Des exemples et de brèves discussions sur les citations de paragraphe et les citations imbriquées peuvent être trouvés ici http: //en.wikipedia .org / wiki / Quotation_mark .

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top