Je ne fais pas ce métier pour gagner ma vie alors pardonnez-moi s’il s’agit d’une question simple (ou plus compliquée que je ne le pense). J’ai fouillé dans les archives et j’ai trouvé de nombreux conseils pratiques, mais étant novice, je ne sais pas trop comment modifier mes besoins ou ceux-ci dépassent mon entendement.
J'ai de gros fichiers de données que je peux analyser pour générer une liste de coordonnées essentiellement séquentielles.
5
6
7
8
15
16
17
25
26
27
Ce que je veux, c'est une liste des lacunes
1-4
9-14
18-24
Je ne sais pas Perl ,SQLni rien d’exceptionnel, mais je pensais pouvoir faire quelque chose qui soustrait un nombre au suivant. Je pourrais alors au moins grep
le résultat où la différence n'était pas 1 ou -1 et travailler avec cela pour combler les lacunes.
Avec awk :
awk '$1!=p+1{print p+1"-"$1-1}{p=$1}' file.txt
$1
est la première colonne de la ligne d'entrée actuellep
est la valeur précédente de la dernière ligne($1!=p+1)
est une condition: si $1
est différent de la valeur précédente +1, alors:{print p+1 "-" $1-1}
: affiche la valeur précédente +1, le caractère -
et les premières colonnes + 1{p=$1}
est exécuté pour chaque ligne: p
est affecté à la 1ère colonne actuelleRappelez-vous simplement le numéro précédent et vérifiez que le numéro actuel est le précédent plus un:
#! /bin/bash
previous=0
while read n ; do
if (( n != previous + 1 )) ; then
echo $(( previous + 1 ))-$(( n - 1 ))
fi
previous=$n
done
Vous devrez peut-être ajouter des vérifications pour empêcher des lignes telles que 28-28
pour les espaces à un seul chiffre.
question interessante.
la doublure awk de Sputnick est Nice. Je ne peux pas en écrire un plus simple que le sien. Je viens d'ajouter une autre façon en utilisant diff:
seq $(tail -1 file)|diff - file|grep -Po '.*(?=d)'
la sortie avec votre exemple serait:
1,4
9,14
18,24
Je savais qu'il y avait une virgule dedans, au lieu de -
. vous pouvez remplacer le grep par sed pour obtenir -
; grep ne peut pas changer le texte saisi ... mais l'idée est la même.
j'espère que ça aide.
Peut-être que quelqu'un d'autre peut vous donner la solution Bash ou Awk que vous avez demandée. Cependant, je pense que toute réponse basée sur Shell est susceptible d'être extrêmement localisée pour votre ensemble de données et peu extensible. La résolution du problème dans Ruby est assez simple et vous offre une mise en forme flexible et davantage d'options pour manipuler le jeu de données d'une autre manière. YMMV.
#!/usr/bin/env Ruby
# You could read from a file if you prefer,
# but this is your provided corpus.
nums = [5, 6, 7, 8, 15, 16, 17, 25, 26, 27]
# Find gaps between zero and first digit.
nums.unshift 0
# Create array of arrays containing missing digits.
missing_nums = nums.each_cons(2).map do |array|
(array.first.succ...array.last).to_a unless
array.first.succ == array.last
end.compact
# => [[1, 2, 3, 4], [9, 10, 11, 12, 13, 14], [18, 19, 20, 21, 22, 23, 24]]
# Format the results any way you want.
puts missing_nums.map { |ary| "#{ary.first}-#{ary.last}" }
Compte tenu de votre corpus actuel, ceci donne les résultats suivants sur la sortie standard:
1-4
9-14
18-24
Étant donné l'entrée fichier , utilisez les numinterval
util et paste
ses sorties à côté de file , puis attribuez-lui tr
, xargs
, sed
et printf
:
gaps() { paste <(echo; numinterval "$1" | tr 1 '-' | tr -d '[02-9]') "$1" |
tr -d '[:blank:]' | xargs echo |
sed 's/ -/-/g;s/-[^ ]*-/-/g' | xargs printf "%s\n" ; }
Sortie de gaps file
:
5-8
15-17
25-27
Comment ça marche. La sortie de paste <(echo; numinterval file) file
ressemble à ceci:
5
1 6
1 7
1 8
7 15
1 16
1 17
8 25
1 26
1 27
À partir de là, nous remplaçons principalement les éléments de la colonne 1 et modifions l'espacement. Les 1
s sont remplacés par -
s et les nombres les plus élevés sont vides. Supprimez quelques espaces avec tr
. Remplacez les passages de traits d'union tels que " 5-6-7-8 " par un seul trait d'union " 5-8 ", et c'est la sortie.
Solution Perl similaire à la solution awk de StardustOne:
Perl -ane 'if ($F[0] != $p+1) {printf "%d-%d\n",$p+1,$F[0]-1}; $p=$F[0]' file.txt
Ces options de ligne de commande sont utilisées:
-n
boucle autour de chaque ligne du fichier d'entrée, ne pas imprimer automatiquement chaque ligne
-a
mode autosplit - divise les lignes d’entrée dans le tableau @F. La valeur par défaut est la division sur les espaces blancs. Les champs sont indexés à partir de 0.
-e
exécuter le code Perl