Comment fusionner des lignes d’une même colonne à l’aide des outils unix

J’ai un fichier texte qui ressemble à ceci:

1000000 45 M This is a line This is another line Another line that breaks into that also breaks that has a blank multiple rows into multiple rows - row below. How annoying! 1000001 50 FI am another I am well behaved. column that has text spanning multiple rows 

Je voudrais convertir cela en un fichier csv qui ressemble à:

 1000000, 45, M, This is a line that breaks into multiple rows, This is another line that also breaks into multiple rows - How annoying! 1000001, 50, F, I am another column that has text spanning multiple rows, I am well behaved. 

La sortie du fichier texte provient d’un programme écrit en 1984 et je n’ai aucun moyen de modifier la sortie. Je le veux au format csv pour que je puisse le convertir en Excel aussi facilement que possible. Je ne sais pas par où commencer et plutôt que de réinventer la roue, j’espérais que quelqu’un pourrait me diriger dans la bonne direction. Merci!

== EDIT ==

J’ai modifié le fichier texte pour avoir \n entre les lignes – cela pourrait peut-être être utile?

== EDIT 2 ==

J’ai modifié le fichier texte pour avoir une ligne vide.

Utiliser GNU awk

 gawk ' BEGIN { FIELDWIDTHS="11 6 5 22 22" } length($1) == 11 { if ($1 ~ /[^[:blank:]]/) { if (f1) print_line() f1=$1; f2=$2; f3=$3; f4=$4; f5=$5 } else { f4 = f4" "$4; f5 = f5" "$5 } } function rsortingm(str) { sub(/[[:blank:]]+$/, "", str) return str } function print_line() { gsub(/[[:blank:]]{2,}/, " ", f4); gsub(/"/, "&&", f4) gsub(/[[:blank:]]{2,}/, " ", f5); gsub(/"/, "&&", f5) printf "%s,%s,%s,\"%s\",\"%s\"\n", rsortingm(f1), rsortingm(f2), rsortingm(f3),f4,f5 } END {if (f1) print_line()} ' file 
 1000000,45,M,"This is a line that breaks into multiple rows ","This is another line that also breaks into multiple rows - How annoying!" 1000001,50,F,"I am another column that has text spanning multiple rows","I am well behaved. " 

J’ai cité les deux dernières colonnes au cas où elles contiendraient des virgules et doublé les éventuels doubles guillemets internes.

Voici un script Perl qui fait ce que vous voulez. Il utilise unpack pour fractionner les colonnes de largeur fixe en champs, en ajoutant aux champs précédents s’il n’y a pas de données dans la première colonne.

Comme vous avez mentionné que les largeurs varient entre les fichiers, le script calcule les largeurs pour lui-même, en fonction du contenu de la première ligne. L’hypothèse est qu’il y a au moins deux espaces entre chaque champ. Il crée une chaîne de format comme A11 A6 A5 A22 A21 , où “A” signifie n’importe quel caractère et les nombres spécifient la largeur de chaque champ.

Inspiré par la version de glenn, j’ai enveloppé tous les champs contenant des espaces entre guillemets. Que cela soit utile ou non dépend de la manière dont vous allez utiliser les données. Par exemple, si vous souhaitez l’parsingr à l’aide d’un autre outil et que l’entrée contient des virgules, cela peut être utile. Si vous ne voulez pas que cela se produise, vous pouvez changer le bloc grep aux deux endroits pour simplement grep { $_ ne "" } :

 use ssortingct; use warnings; chomp (my $first_line = <>); my @fields = split /(?<=\s{2})(?=\S)/, $first_line; my $format = join " ", map { "A" . length } @fields; my @cols = unpack $format, $first_line; while(<>) { chomp( my $line = $_ ); my @tmp = unpack $format, $line; if ($tmp[0] ne '') { print join(", ", grep { $_ ne "" && /\s/ ? qq/"$_"/ : $_ } @cols), "\n"; @cols = @tmp; } else { for (1..$#tmp) { $cols[$_] .= " $tmp[$_]" if $tmp[$_] ne ""; } } } print join(", ", grep { $_ ne "" && /\s/ ? qq/"$_"/ : $_ } @cols), "\n"; 

Sortie:

 1000000, 45, M, "This is a line that breaks into multiple rows", "This is another line that also breaks into multiple rows - How annoying!" 1000001, 50, F, "I am another column that has text spanning multiple rows", "I am well behaved." 

En utilisant cet awk :

 awk -F ' {2,}' -v OFS=', ' 'NF==5{if (p) print a[1], a[2], a[3], a[4], a[5]; for (i=1; i<=NF; i++) a[i]=$i; p=index($0,$4)} NF<4 {for(i=2; i<=NF; i++) index($0,$i) == p ? a[4]=a[4] " " $i : a[5]=a[5] $i} END { print a[1], a[2], a[3], a[4], a[5] }' file 1000000, 45, M, This is a line that breaks into multiple rows, This is another line that also breaks into multiple rows - How annoying! 1000001, 50, F, I am another column that has text spanning multiple rows, I am well behaved. 

Vous pouvez écrire un script en python qui fait cela. Lisez chaque ligne, appelez-la, si la ligne n’est pas vide, ajoutez-la à la ligne précédente. Si c’est le cas, ajoutez la ligne suivante au jeu de résultats. Enfin, utilisez csv write pour écrire le jeu de résultats dans un fichier.

Quelque chose du genre:

 #import csv inputFile = open(filename, 'r') isNewItem = True results = [] for line in inputFile: if len(results) == 0: isNewItem = True else if line == '': isNewItem = True continue else: inNewItem = False temp = line.split() if isNewItem: results.append(temp) else lastRow = results[-1] combinedRow = [] for leftColumn, rigtColumn in lastRow, temp: combinedRow.append(leftColumn + rightColumn) with open(csvOutputFileName, 'w') as outFile: csv.write(results)