web-dev-qa-db-fra.com

Trouver des lacunes dans les nombres séquentiels

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.

30
Shaun

Avec awk :

awk '$1!=p+1{print p+1"-"$1-1}{p=$1}' file.txt

explications

  • $1 est la première colonne de la ligne d'entrée actuelle
  • p est la valeur précédente de la dernière ligne
  • alors ($1!=p+1) est une condition: si $1 est différent de la valeur précédente +1, alors:
  • cette partie est exécutée: {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 actuelle
62
Gilles Quenot

Rappelez-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.

2
choroba

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.

2
Kent

Une réponse en rubis

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

2
Todd A. Jacobs

É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 1s 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.

0
agc

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

0
Chris Koknat