J'ai un énorme fichier csv avec 10 champs séparés par des virgules. Malheureusement, certaines lignes sont malformées et ne contiennent pas exactement 10 virgules (ce qui cause des problèmes lorsque je veux lire le fichier dans R). Comment filtrer uniquement les lignes qui contiennent exactement 10 virgules?
9
sed
cas ici) seulement jusqu'à une correspondance de plus que ce qui est recherché, bien que cette question le fasse. Vous ne devriez pas avoir fermé cela.grep
réponse n'est pas une réponse acceptable pour l'une ou l'autre question ...Réponses:
Un autre POSIX:
Si la ligne a 10 virgules, alors il y aura 11 champs dans cette ligne. Nous faisons donc tout simplement
awk
utiliser,
comme séparateur de champ. Si le nombre de champs est de 11, la conditionNF == 11
est vraie,awk
puis effectue l'action par défautprint $0
.la source
-F
définit le séparateur de champs etNF
fait référence au nombre de champs dans une ligne donnée. Puisqu'aucun bloc de code{statement}
n'est ajouté à la conditionNF == 11
, l'action par défaut consiste à imprimer la ligne. (@cuonglm, n'hésitez pas à incorporer cette explication si vous le souhaitez.)awk -F , 'NF != 11' <file
-
ou nommé-
.En utilisant
egrep
(ougrep -E
en POSIX):Cela filtre tout ce qui ne contient pas 10 virgules: il correspond aux lignes complètes (
^
au début et$
à la fin), contenant exactement dix répétitions ({10}
) de la séquence "n'importe quel nombre de caractères sauf", ", suivi d'un seul", "" (([^,]*,)
), suivi à nouveau par n'importe quel nombre de caractères à l'exception de ',' ([^,]*
).Vous pouvez également utiliser le
-x
paramètre pour supprimer les ancres:Ceci est moins efficace que cuonglm de
awk
solution si; ce dernier est généralement six fois plus rapide sur mon système pour les lignes avec environ 10 virgules. Des lignes plus longues entraîneront d'énormes ralentissements.la source
Le
grep
code le plus simple qui fonctionnera:Explication:
-x
garantit que le motif doit correspondre à la ligne entière , plutôt qu'à une partie seulement. Ceci est important pour ne pas faire correspondre les lignes avec plus de 10 virgules.-E
signifie "regex étendu", ce qui permet de réduire l'échappement anti-slash dans votre regex.Les parenthèses sont utilisées pour le regroupement, ce qui
{10}
signifie par la suite qu'il doit y avoir exactement dix correspondances dans une rangée du motif dans les parenthèses.[^,]
est une classe de caractères — par exemple,[c-f]
correspondrait à n'importe quel caractère unique qui est ac
, ad
, ane
ou anf
, et[^A-Z]
correspondrait à tout caractère unique qui n'est PAS une lettre majuscule. Correspond donc à[^,]
n'importe quel caractère, à l'exception d'une virgule.L'
*
après la classe de caractères signifie «zéro ou plusieurs d'entre eux».Ainsi, la partie regex
([^,]*,)
signifie "N'importe quel caractère sauf une virgule un certain nombre de fois (y compris zéro fois), suivi d'une virgule" et le{10}
spécifie 10 d'entre eux. Ensuite,[^,]*
pour faire correspondre le reste des caractères non virgule à la fin de la ligne.la source
Cela ramifie d'abord toute ligne avec 11 virgules ou plus, puis imprime ce qui reste uniquement ceux qui correspondent à 10 virgules.
Apparemment, j'ai répondu à cela avant ... Voici un plagiat à partir d'une question recherchant exactement 4 occurrences d'un modèle:
la source
s/hello/world/2
pars//world/2
, GNU sed fonctionne très bien. Avec deuxsed
de l'héritage,/usr/5bin/posix/sed
soulevez segfault,/usr/5bin/sed
passe en boucle infinitive.sed
etawk
(dans les commentaires) —J'aime cette réponse et j'ai voté pour elle, mais notez que la traduction de laawk
réponse acceptée est: "Imprimer les lignes avec 11 champs" et la traduction de cettesed
réponse est: " Essayez de supprimer la 11e virgule; passez à la ligne suivante si vous échouez. Essayez de remplacer la 10e virgule par elle-même; imprimez la ligne si vous réussissez. " Laawk
réponse donne les instructions à l'ordinateur exactement comme vous les exprimeriez en anglais. (awk
convient aux données basées sur le terrain.)Jeter un court
python
:Cela lira chaque ligne et vérifiera si le nombre de virgules dans la ligne est égal à 10
line.count(',') == 10
, si c'est le cas, imprimez la ligne.la source
Et voici une façon Perl:
La
-n
causeperl
lit son fichier d'entrée ligne par ligne et exécute le script donné par-e
sur chaque ligne. L'-a
activation du fractionnement automatique est activée: chaque ligne d'entrée sera divisée selon la valeur donnée par-F
(ici, une virgule) et enregistrée en tant que tableau@F
.Le
$#F
(ou, plus généralement$#array
), est l'indice le plus élevé du tableau@F
. Puisque les tableaux commencent à0
, une ligne avec 11 champs aura un@F
de10
. Le script imprime donc la ligne s'il contient exactement 11 champs.la source
print if @F==11
comme un tableau dans un contexte scalaire renvoie le nombre d'éléments.Si les champs peuvent contenir des virgules ou des sauts de ligne, votre code doit comprendre csv. Exemple (avec trois colonnes):
Je suppose que la plupart des solutions jusqu'à présent élimineraient les deuxième et quatrième rangées.
la source