Je sais que cela (ou similaire) a été demandé à plusieurs reprises, mais après avoir essayé de nombreuses possibilités, je n'ai pas été en mesure de trouver une regex qui fonctionne à 100%.
J'ai un fichier CSV et j'essaie de le scinder en un tableau, mais je rencontre deux problèmes: des virgules et des éléments vides.
Le CSV ressemble à:
123,2.99,AMO024,Title,"Description, more info",,123987564
La regex que j'ai essayé d'utiliser est:
thisLine.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)
Le seul problème est que dans mon tableau de sortie, le 5ème élément est le 123987564 et non une chaîne vide.
Au lieu d'utiliser une scission, je pense qu'il serait plus facile de simplement exécuter une correspondance et de traiter toutes les correspondances trouvées.
Cette expression va:
Regex: (?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)
Exemple de texte
123,2.99,AMO024,Title,"Description, more info",,123987564
ASP exemple utilisant l'expression non Java
Set regEx = New RegExp
regEx.Global = True
regEx.IgnoreCase = True
regEx.MultiLine = True
sourcestring = "your source string"
regEx.Pattern = "(?:^|,)(?=[^""]|("")?)""?((?(1)[^""]*|[^,""]*))""?(?=,|$)"
Set Matches = regEx.Execute(sourcestring)
For z = 0 to Matches.Count-1
results = results & "Matches(" & z & ") = " & chr(34) & Server.HTMLEncode(Matches(z)) & chr(34) & chr(13)
For zz = 0 to Matches(z).SubMatches.Count-1
results = results & "Matches(" & z & ").SubMatches(" & zz & ") = " & chr(34) & Server.HTMLEncode(Matches(z).SubMatches(zz)) & chr(34) & chr(13)
next
results=Left(results,Len(results)-1) & chr(13)
next
Response.Write "<pre>" & results
Correspondances utilisant l'expression non Java
Le groupe 0 obtient la sous-chaîne entière qui inclut la virgule
Le groupe 1 obtient la citation si elle est utilisée
Le groupe 2 obtient la valeur sans la virgule
[0][0] = 123
[0][1] =
[0][2] = 123
[1][0] = ,2.99
[1][1] =
[1][2] = 2.99
[2][0] = ,AMO024
[2][1] =
[2][2] = AMO024
[3][0] = ,Title
[3][1] =
[3][2] = Title
[4][0] = ,"Description, more info"
[4][1] = "
[4][2] = Description, more info
[5][0] = ,
[5][1] =
[5][2] =
[6][0] = ,123987564
[6][1] =
[6][2] = 123987564
J'ai créé cela il y a quelques mois pour un projet.
".+?"|[^"]+?(?=,)|(?<=,)[^"]+
Cela fonctionne en C # et Debuggex était heureux lorsque j'ai sélectionné Python et PCRE. Javascript ne reconnaît pas cette forme de Proceeded By ? <= ....
Pour vos valeurs, il créera des correspondances sur
123
,2.99
,AMO024
,Title
"Description, more info"
,
,123987564
Notez que tout ce qui est entre guillemets ne comporte pas de virgule, mais une tentative de correspondance avec une virgule était requise pour le cas d'utilisation de valeur vide. Une fois cela fait, réduisez les valeurs si nécessaire.
J'utilise RegexHero.Net pour tester mon regex.
J'avais besoin de cette réponse aussi, mais j'ai trouvé les réponses informatives, un peu difficiles à suivre et à reproduire pour d'autres langues. Voici l'expression la plus simple que j'ai trouvée pour une seule colonne de la ligne CSV. Je ne me sépare pas. Je construis une expression rationnelle pour faire correspondre une colonne du CSV, donc je ne divise pas la ligne:
("([^"]*)"|[^,]*)(,|$)
Cela correspond à une seule colonne de la ligne CSV. La première partie "([^"]*)"
de l'expression doit correspondre à une entrée entre guillemets, la deuxième partie [^,]*
doit correspondre à une entrée sans guillemet Ensuite, suivi d'un ,
ou de la fin de la ligne $
.
Et le debuggex qui l'accompagne pour tester l'expression.
Travaillé un peu sur ce sujet et proposé cette solution:
(?:,|\n|^)("(?:(?:"")*[^"]*)*"|[^",\n]*|(?:\n|$))
Cette solution gère les "belles" données CSV comme
"a","b",c,"d",e,f,,"g"
0: "a"
1: "b"
2: c
3: "d"
4: e
5: f
6:
7: "g"
et des choses plus laides comme
"""test"" one",test' two,"""test"" 'three'","""test 'four'"""
0: """test"" one"
1: test' two
2: """test"" 'three'"
3: """test 'four'"""
Voici une explication de comment cela fonctionne :
(?:,|\n|^) # all values must start at the beginning of the file,
# the end of the previous line, or at a comma
( # single capture group for ease of use; CSV can be either...
" # ...(A) a double quoted string, beginning with a double quote (")
(?: # character, containing any number (0+) of
(?:"")* # escaped double quotes (""), or
[^"]* # non-double quote characters
)* # in any order and any number of times
" # and ending with a double quote character
| # ...or (B) a non-quoted value
[^",\n]* # containing any number of characters which are not
# double quotes ("), commas (,), or newlines (\n)
| # ...or (C) a single newline or end-of-file character,
# used to capture empty values at the end of
(?:\n|$) # the file or at the ends of lines
)
Je suis en retard à la fête, mais voici l'expression régulière que j'utilise:
(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)
Ce modèle comporte trois groupes de capture:
Ce modèle gère tous les éléments suivants:
Voir ce modèle en cours d'utilisation.
Si vous utilisez un type plus rationnel de regex avec des groupes nommés et des recherches, je préfère ce qui suit:
(?<quoted>(?<=,"|^")(?:""|[\w\W]*?)*(?=",|"$))|(?<normal>(?<=,(?!")|^(?!"))[^,]*?(?=(?<!")$|(?<!"),))|(?<eol>\r\n|\n)
Voir ce modèle en cours d'utilisation.
Modifier
(?:^"|,")(""|[\w\W]*?)(?=",|"$)|(?:^(?!")|,(?!"))([^,]*?)(?=$|,)|(\r\n|\n)
Ce modèle légèrement modifié gère les lignes où la première colonne est vide tant que vous n'utilisez pas Javascript. Pour une raison quelconque, Javascript omettra la deuxième colonne avec ce modèle. Je suis incapable de gérer correctement ce cas Edge.
J'ai personnellement essayé de nombreuses expressions RegEx sans avoir trouvé celle qui convient à tous les cas.
Je pense que les expressions rationnelles sont difficiles à configurer correctement pour correspondre à tous les cas correctement. résultats corrects tous les temps dans tous les cas (principalement gérer très bien chaque cas de guillemets doubles):
Microsoft.VisualBasic.FileIO.TextFieldParser
Je l'ai trouvé ici: StackOverflow
Exemple d'utilisation:
TextReader textReader = new StringReader(simBaseCaseScenario.GetSimStudy().Study.FilesToDeleteWhenComplete);
Microsoft.VisualBasic.FileIO.TextFieldParser textFieldParser = new TextFieldParser(textReader);
textFieldParser.SetDelimiters(new string[] { ";" });
string[] fields = textFieldParser.ReadFields();
foreach (string path in fields)
{
...
J'espère que ça pourrait aider.
L'avantage d'utiliser JScript pour les pages classiques ASP est que vous pouvez utiliser l'une des nombreuses bibliothèques écrites pour JavaScript.
Comme celui-ci: https://github.com/gkindel/CSV-JS . Téléchargez-le, incluez-le dans votre page ASP, analysez-le au format CSV.
<%@ language="javascript" %>
<script language="javascript" runat="server" src="scripts/csv.js"></script>
<script language="javascript" runat="server">
var text = '123,2.99,AMO024,Title,"Description, more info",,123987564',
rows = CSV.parse(line);
Response.Write(rows[0][4]);
</script>
En Java, ce motif ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))"
ou presque travaille pour moi:
String text = "\",\",\",,\",,\",asdasd a,sd s,ds ds,dasda,sds,ds,\"";
String regex = ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))";
Pattern p = Pattern.compile(regex);
String[] split = p.split(text);
for(String s:split) {
System.out.println(s);
}
sortie:
","
",a,,"
",asdasd a,sd s,ds ds,dasda,sds,ds,"
Inconvénient: ne fonctionne pas lorsque la colonne a un nombre impair de guillemets :(
Aaaand une autre réponse ici. :) Puisque je ne pouvais pas faire fonctionner les autres tout à fait.
Ma solution gère à la fois les guillemets échappés (occurrences doubles) et n'inclut pas de délimiteurs dans la correspondance.
Notez que j'ai comparé avec '
au lieu de "
car c'était mon scénario, mais remplacez-les simplement dans le motif pour le même effet.
Voilà (n'oubliez pas d'utiliser l'indicateur "Ignorer les espaces blancs" /x
si vous utilisez la version commentée ci-dessous):
# Only include if previous char was start of string or delimiter
(?<=^|,)
(?:
# 1st option: empty quoted string (,'',)
'{2}
|
# 2nd option: nothing (,,)
(?:)
|
# 3rd option: all but quoted strings (,123,)
# (included linebreaks to allow multiline matching)
[^,'\r\n]+
|
# 4th option: quoted strings (,'123''321',)
# start pling
'
(?:
# double quote
'{2}
|
# or anything but quotes
[^']+
# at least one occurance - greedy
)+
# end pling
'
)
# Only include if next char is delimiter or end of string
(?=,|$)
Version à une ligne:
(?<=^|,)(?:'{2}|(?:)|[^,'\r\n]+|'(?:'{2}|[^']+)+')(?=,|$)
J'utilise celui-ci, il fonctionne avec le séparateur de coma et les guillemets doubles . Normalement, cela devrait résoudre votre problème:
/(?<=^|,)(\"(?:[^"]+|"")*\"|[^,]*)(?:$|,)/g
Encore une autre réponse avec quelques fonctionnalités supplémentaires, telles que la prise en charge des valeurs entre guillemets contenant des guillemets et des caractères CR/LF (valeurs uniques couvrant plusieurs lignes).
NOTE: Bien que la solution ci-dessous puisse probablement être adaptée à d'autres moteurs regex, son utilisation en l'état nécessitera que votre moteur regex traite plusieurs groupes de capture nommés utilisant le même nom as un seul groupe de capture. (.NET le fait par défaut)
Lorsque plusieurs lignes/enregistrements d'un fichier/flux CSV (correspondant norme RFC 4180 ) sont passés à l'expression régulière ci-dessous, une correspondance est trouvée pour chaque ligne/enregistrement non vide. Chaque correspondance contiendra un groupe de capture nommé Value
qui contient les valeurs capturées dans cette ligne/enregistrement (et éventuellement un groupe de capture OpenValue
s'il y avait un devis ouvert à la fin de la ligne/enregistrement).
Voici le motif commenté (testez-le sur Regexstorm.net ):
(?<=\r|\n|^)(?!\r|\n|$) // Records start at the beginning of line (line must not be empty)
(?: // Group for each value and a following comma or end of line (EOL) - required for quantifier (+?)
(?: // Group for matching one of the value formats before a comma or EOL
"(?<Value>(?:[^"]|"")*)"| // Quoted value -or-
(?<Value>(?!")[^,\r\n]+)| // Unquoted value -or-
"(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)| // Open ended quoted value -or-
(?<Value>) // Empty value before comma (before EOL is excluded by "+?" quantifier later)
)
(?:,|(?=\r|\n|$)) // The value format matched must be followed by a comma or EOL
)+? // Quantifier to match one or more values (non-greedy/as few as possible to prevent infinite empty values)
(?:(?<=,)(?<Value>))? // If the group of values above ended in a comma then add an empty value to the group of matched values
(?:\r\n|\r|\n|$) // Records end at EOL
Voici le motif brut sans tous les commentaires ni les espaces.
(?<=\r|\n|^)(?!\r|\n|$)(?:(?:"(?<Value>(?:[^"]|"")*)"|(?<Value>(?!")[^,\r\n]+)|"(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|(?<Value>))(?:,|(?=\r|\n|$)))+?(?:(?<=,)(?<Value>))?(?:\r\n|\r|\n|$)
Voici une visualisation de Debuggex.com (groupes de capture nommés pour plus de clarté):
Vous trouverez des exemples d'utilisation du motif regex dans ma réponse à une question similaire ici , ou sur pavé C # ici ou ici .
,?\s*'.+?'|,?\s*".+?"|[^"']+?(?=,)|[^"']+
Cette expression rationnelle fonctionne avec des guillemets simples et doubles et aussi pour une citation dans une autre!
Si vous savez que vous n'avez pas de champ vide (,), cette expression fonctionne bien:
("[^"]*"|[^,]+)
Comme dans l'exemple suivant ...
Set rx = new RegExp
rx.Pattern = "(""[^""]*""|[^,]+)"
rx.Global = True
Set col = rx.Execute(sText)
For n = 0 to col.Count - 1
if n > 0 Then s = s & vbCrLf
s = s & col(n)
Next
Toutefois, si vous prévoyez un champ vide et que votre texte est relativement petit, vous pouvez envisager de remplacer les champs vides par un espace avant l'analyse afin de garantir leur capture. Par exemple...
...
Set col = rx.Execute(Replace(sText, ",,", ", ,"))
...
Et si vous devez maintenir l'intégrité des champs, vous pouvez restaurer les virgules et tester les espaces vides à l'intérieur de la boucle. Ce n’est peut-être pas la méthode la plus efficace, mais le travail est fait.
Si j'essaye l'expression régulière publiée par @chubbsondubs sur http://regex101.com en utilisant l'indicateur 'g', il existe des correspondances contenant uniquement ',' ou une chaîne vide . Avec cette expression régulière:
(?:"([^"]*)"|([^,]*))(?:[,])
Je peux faire correspondre les parties du CSV (y compris les parties citées). (La ligne doit être terminée par un ',' sinon la dernière partie n'est pas reconnue.)
https://regex101.com/r/dF9kQ8/4
Si le CSV ressemble à:"",huhu,"hel lo",world,
il y a 4 correspondances:
''
'huhu'
'Bonjour'
'monde'
Celui-ci correspond à tout ce dont j'ai besoin en c #:
(?<=(^|,)(?<quote>"?))([^"]|(""))*?(?=\<quote>(?=,|$))
J'avais le même besoin de fractionner les valeurs CSV à partir d'instructions SQL.
Dans mon cas, je pouvais supposer que les chaînes étaient entourées de guillemets simples et que les chiffres ne l'étaient pas.
csv.split(/,((?=')|(?=\d))/g).filter(function(x) { return x !== '';});
Pour une raison probablement évidente, cette expression régulière produit des résultats vierges. Je pouvais les ignorer, car toutes les valeurs vides de mes données étaient représentées par ...,'',...
et non pas ...,,...
.