2009-06-22 4 views
1

J'ai besoin d'une regex qui va analyser un fichier de style CSV, quelque chose comme 57 champs de large, la plupart des champs entre guillemets (mais pas tous), séparés par des virgules, avec des champs quotés ("") quotes dans la chaîne évaluée. Je suis un débutant/intermédiaire regex, et je pense que je peux obtenir assez rapidement à l'expression de base pour faire l'analyse du champ, mais ce sont les guillemets intégrés (et les virgules) Je ne peux pas obtenir ma tête .Regex, disposition de type csv, permet "" à l'intérieur des chaînes entre guillemets?

Quelqu'un? (Pas que cela soit important, mais le langage spécifique est Matlab.)

+0

J'ai essayé auparavant de faire ceci en utilisant Perl, mais échoué et suis allé pour une approche programmatique. regex n'est pas vraiment adapté à l'analyse, plus à l'appariement. Bonne chance pour trouver une approche. N'y a-t-il pas une bibliothèque d'analyse CSV pour Matlab? – Xetius

+0

PS. désolé de donner cette réponse "Ne faites pas comme ça" et n'offrir aucun début, mais c'est vraiment une chose si compliquée à aborder avec une expression régulière. – Xetius

Répondre

1

Si vous devez vraiment le faire avec une regex, je le ferais en deux passes; tout d'abord séparer les champs en se divisant sur les virgules avec quelque chose comme:

regexp(theString, '(?<!\\),', 'split'); 

Cela devrait se séparer sur des virgules, seulement quand il n'y a pas une barre oblique précédente (je suppose que c'est ce que vous entendez par des virgules échappées) . (Je pense que dans Matlab vous vous retrouverez avec un tableau d'index dans les cordes d'origine)

alors vous devriez vérifier chaque champ adapté pour les citations échappées, et les remplacer par quelque chose comme:

regexprep(individualString, '""', '"'); 

De même pour des virgules:

regexprep(individualString, '\\,', ','); 

Je ne suis pas sûr de la double échappé de l \ 'Matlab n'ayant pas eu beaucoup d'expérience avec elle.

Comme d'autres l'ont dit, il est probablement préférable d'utiliser une bibliothèque csv pour gérer le fichier initial.

+0

Nous avons fini par itérer article par article sur chaque ligne, en commençant par le faire correspondre puis en le remplaçant; donc cela obtient le contrôle pour être le plus proche. Mais voir ma réponse affichée. –

+0

Est-ce que cela fonctionne pour les virgules entre guillemets? Je n'ai jamais pu obtenir des expressions rationnelles pour travailler avec des chaînes quotées. –

+0

La première expression rationnelle sera divisée sur toutes les virgules qui ne sont pas précédées d'une barre oblique inverse (à séparer en champs). Donc, à moins que toutes les virgules soient correctement échappées, vous pourriez obtenir des résultats intéressants! C'est une regexp très simple (probablement trop simple :)), elle ne cherche pas à vérifier quoi que ce soit entre guillemets – owst

1

échapper les guillemets -? le rend facultatif.

\"? 
+0

Mais n'ai-je pas besoin d'identifier les guillemets environnants dans la regex? C'est la partie que je suis confus. Une valeur de chaîne sera entourée de guillemets et séparée des autres champs par des virgules, mais une telle valeur peut contenir des bits de guillemets doubles et des virgules incorporées. Je ne peux juste pas voir comment écrire celui-ci. –

4

Je sais qu'il ya i grand battage médiatique autour des expressions régulières de nos jours, mais je recommande vraiment utiliser une bibliothèque pour les tâches qui ont déjà été mises en œuvre par d'autres - il sera plus facile à mettre en œuvre, plus facile à lire et plus facile à maintenir (Voulez-vous lire les csv séparés par des guillemets la prochaine fois? La bibliothèque peut le faire, mais votre regex aura besoin d'une réécriture). Un rapide google search devrait vous donner un bon début.

+1

D'accord. Vous pouvez regarder sur MATLAB Central pour les fichiers M-code, mais ils ont tendance à être immatures. Mais Matlab peut facilement intégrer de nombreuses bibliothèques Java. J'ai eu de la chance en utilisant la bibliothèque Java OpenCSV dans Matlab, en écrivant un léger wrapper M-code pour cela. –

0

Il m'a fallu un certain temps pour que cela fonctionne, puisque beaucoup d'expressions rationnelles sur le net ne gèrent pas une partie ou l'autre. Voici le code dans F # /. NET. Désolé, mais je ne parle pas Matlab:

let splitCsv (s:string) = 
    let re = new Regex("\\s*((?:\"(?:(?:\"\")|[^\"])*\")|[^\"]*?)\\s*(?:,|$)") 

    re.Matches(s + " ") 
    |> Seq.cast<Match> 
    |> Seq.map (fun m -> m.Groups.[1].Value) 
    |> Seq.map (fun s -> s.Replace("\"\"", "\"")) 
    |> Seq.map (fun s -> s.Trim([| '"'; ' ' |])) 
    |> List.of_seq 

Cette version poignées chaînes encadrées, guillemets échappés comme des guillemets doubles, et les garnitures supplémentaires (se sont échappés) des citations et des espaces autour de la chaîne entière (original: « Test », double-quoted: "" "Test" ""). Il gère également correctement un champ vide dans la dernière position (d'où le s + "") et il gère aussi correctement les virgules dans les chaînes entre guillemets.

0

Merci pour les réponses. Cas classique du débutant pensant que le problème est facile, les experts sachant que le problème est difficile. Après avoir lu vos articles, j'ai cherché une bibliothèque d'analyseurs csv en conserve dans Matlab; trouvé un couple, ni l'un ni l'autre qui pourrait faire le travail (d'abord essayé de faire le fichier entier à la fois, a échoué sur la mémoire, la deuxième a échoué à mon bugaboo spécifique, guillemets doublés dans une chaîne entre guillemets).

Alors nous avons roulé les nôtres, avec l'aide d'une regex trouvée sur le web et modifiée. Reste à être déplacé vers Matlab mais le code Python est la suivante:

import re 

text = ["<omitted>"] 

# Regex: empty before comma OR string w/ no quote or comma OR quote-surrounded string w/ optional doubles 
p = re.compile('(?=,)|[^",]+|"(?:[^"]|"")*"') 

for line in text: 
    print 'Line: %s' % line 
    m = p.search(line)         
    fld = 1 
    while m:            
     val = m.group().strip('"').replace('""', '"') 
     print 'Field %d: %s' % (fld, val) 
     line = re.sub(p, '', line, 1)   
     if line and line[0] == ',':   
      line = line[1:] 
     fld += 1 
     m = p.search(line)     
    print 
0

Page 271 de Friedl Mastering Regular Expressions a une expression régulière pour extraire éventuellement des champs entre guillemets CSV, mais il faut un peu de post-traitement:

>>> re.findall('(?:^|,)(?:"((?:[^"]|"")*)"|([^",]*))', '"a,b,c",d,e,f') 
[('a,b,c', ''), ('', 'd'), ('', 'e'), ('', 'f')] 
>>> re.findall('(?:^|,)(?:"((?:[^"]|"")*)"|([^",]*))', '"a,b,c",d,,f') 
[('a,b,c', ''), ('', 'd'), ('', ''), ('', 'f')] 

même motif avec le drapeau bavard:

csv = re.compile(r""" 
    (?:^|,) 
    (?: # now match either a double-quoted field 
     # (inside, paired double quotes are allowed)... 
     " # (double-quoted field's opening quote) 
      ( (?: [^"] | "")* ) 
     " # (double-quoted field's closing quote) 
    | 
     # ...or some non-quote/non-comma text... 
     ([^",]*) 
    )""", re.X) 
0

Il est possible de faire usi ng une seule regex avec lookahead. Illustré ici dans perl:

my @rows; 

foreach my $line (@lines) { 

    my @cells; 
    while ($line =~ /(("|').+?\2 | [^,]+?) (?=(,|$))/gx) { 
     push @cells, $1; 
    } 

    push @rows, \@cells; 
} 
Questions connexes