Fractionnement de CSV avec des cellules multilignes

Je travaille avec certains files CSV générés par YouTube (donc je ne peux pas changer la structure de la source). Dans le file CSV, certains loggings couvrent plusieurs lignes. Un exemple hypothétique avec beaucoup d'autres colonnes omises par souci de brièveté est le suivant:

video_id, upload_time, title, policy oHg5SJYRHA0, 2007/05/15, "RickRoll'D", "Monetize in all counsortinges except: CU, IR, KP, SD, SY Track in counsortinges: CU, IR, KP Block in counsortinges: SD, SY" dQw4w9WgXcQ, 2009/10/24, "Rick Astley - Never Gonna Give You Up", "Monetize in all counsortinges except: CU, IR, KP, SD, SY Track in counsortinges: CU, IR, KP, SD, SY" 

Un file typique contient des centaines de milliers d'loggings, sinon des millions d'loggings (un file a une taille de 29,57 Go), ce qui est trop gros pour être traité en une seule fois, donc je voudrais les split en petits morceaux pour les traiter sur des machines séparées . J'ai déjà utilisé split avec -l sur d'autres files de rapport et cela fonctionne très bien quand il n'y a pas de nouvelle ligne dans les cellules. Dans ce cas, si la division se produit sur une mauvaise ligne (par exemple: ligne 4 de l'exemple), alors j'ai cassé des loggings dans deux files. À court d'parsingr le file CSV, puis de le rebuild en plusieurs files, y a-t-il un moyen efficace de scinder les files CSV comme ceci?

Vous allez vouloir parsingr le file CSV pour le ré-émettre en petits morceaux comme vous le souhaitez. Au cours de cette opération, vous voudrez peut-être même la ré-émettre dans un format différent, plus rigoureux et bien défini (comme, oh, je ne sais pas, json).

Votre file d'input est dans un format assez inhabituel. Le module csv de Python , par exemple, ne peut pas l'parsingr, car il possède un délimiteur à plusieurs caractères:, (espace virgule) au lieu du plus commun,. Sinon, vous seriez capable d'parsingr et réémettre sortingvialement le file avec 5 lignes de Python.

Vous devrez find un autre parsingur qui fonctionne, ou en écrire un petit. Tout d'abord, essayez de savoir quels sont les détails du format que vous avez entre vos mains, comme les règles de citation (par exemple, ce qui se passe lorsqu'un champ est cité avec " contient " ).

Vous devrez probablement l'parsingr. Voici un exemple de command grep transposée en trois commands sed qui combinera les strings de plusieurs lignes sur une seule ligne (vous pouvez append un tube pour split -l à la fin):

  grep -Eoz "((([^\",[:space:]]+|\"[!#-~[:space:]]+\"),? ?){4}[[:space:]]){1}" csvtest | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n\n/XXX new record XXX/g' | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' | sed -e "s/XXX new record XXX/\n/g" 

Le briser:

  • L'option -E grep autorise les expressions régulières étendues.
  • L'option -o grep ne produit que les éléments correspondants
  • L'option -z grep traite les caractères de nouvelle ligne sous la forme \0
  • [^\",[:space:]]+ dans le motif correspond aux éléments non cotés
  • \"[!#-~[:space:]]+\" dans le motif correspond aux éléments cités
  • vous devrez peut-être mettre à jour le model des quoted items pour tous les cas spéciaux où les strings citées contiennent des guillemets " ou des gammes de caractères non standard. Ajoutez simplement d'autres gammes de caractères après le ~
  • La première déclaration sed remplace deux nouvelles lignes avec XXX new record XXX . La sortie du grep génère deux nouvelles lignes entre les correspondances.
  • La deuxième instruction sed remplace chaque nouvelle ligne unique restante par un espace.
  • La finale sed remplace le XXX new record XXX ajouté précédemment à une seule nouvelle ligne

Vous pouvez append un split -l pipe à la fin de tout cela.

Pour l'parsing CSV, il est conseillé d'utiliser un parsingur CSV. Avec les versions récentes du module Text :: CSV de Perl, vous pouvez spécifier un séparateur de champs à plusieurs caractères

 #!/usr/bin/env perl use ssortingct; use warnings; use Text::CSV; use Data::Dump; # just for this demonstration # the "binary" option allows newlines in field values my $csv = Text::CSV->new({binary=>1, sep=>", "}) or die Text::CSV->error_diag; open my $fh, "<", "test.csv"; while (my $row = $csv->getline($fh)) { print "next row:\n"; dd $row; # or do something more interesting } close $fh;