trier par valeur hexadécimale


14

À l'aide de coreutils sort, comment puis-je trier numériquement par une valeur hexadécimale (champ)? Je m'attendais à quelque chose dans le sens de

sort -k3,3x file_to_sort

cependant, un tel xn'existe pas.

Edit: La meilleure solution que j'ai trouvée jusqu'à présent est:

{ echo ibase=16; cut -d' ' -f3 file_to_sort; } |
  bc | paste -d: - file_to_sort | sort -t: -k1,1n | cut -d: -f2-

où le cut -d' ' -f3isole le champ de recherche (c'est -k3,3- cela peut varier, bien sûr), et bceffectue la conversion en décimal (nécessite un hex en majuscule, sans 0xpréfixe, correspondant à mon cas). Ensuite, je joins, trie et fractionne des colonnes.


-k3,3? Vous avez des nœuds hexagonaux commençant par 0x et tous de la même longueur? Pas de mélange de majuscules / minuscules? Si oui, ils doivent trier correctement lorsqu'ils sont interprétés comme des chaînes. Peut-être pouvez-vous nous montrer quelques exemples de données?

@yeti: Malheureusement, non.
Stefan

Réponses:


5

Une solution en perl:

$ perl -anle '
    push @h, [$F[-1],$_];
    END {
        print for map  { $_->[0] }
                  sort { $a->[1] <=> $b->[1] }
                  map  { [$_->[1],hex($_->[0])] } @h;
    }
' file
4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12

Explication

  • Pendant le traitement du fichier, nous créons un tableau de tableau @h, chacun de ses éléments est une référence de tableau [$F[-1],$_], avec le premier élément est la valeur hexadécimale à comparer et le deuxième élément est la ligne entière.

  • En ENDbloc, nous utilisons la transformation de Schwartzian :

    • Avec chaque élément de @h, créez un tableau anonyme, contient la ligne entière ( $_->[1]le deuxième élément de chaque tableau ref in @h) et la valeur hexadécimale à comparerhex($_->[0])]

    • Trier au-dessus de la base du tableau sur la valeur hexadécimale $a->[1] <=> $b->[1]

    • Obtenez le premier élément de chaque référence de tableau dans un tableau trié, map { $_->[0] } puis imprimez le résultat.

Mise à jour

Avec la suggestion de @Joseph R, sans utiliser Schwartzian Transform:

$ perl -anle '
    push @h, [hex($F[-1]),$_];
    END {
        print $_->[1] for
            sort { $a->[0] <=> $b->[0] } @h;
    }
' file

Update 2

Après avoir lu le commentaire de Stefan, je pense que cela peut appeler direct:

$ perl -e '
    print sort {hex((split(/\s+/,$a))[-1]) <=> hex((split(/\s+/,$b))[-1])} <>;
' file
4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12

+1 mais pourquoi pas seulement: print for sort { hex $a->[-1] <=> hex $b->[-1] } @h? L' hexopérateur n'est pas assez cher pour justifier un Schwartzian, n'est-ce pas?
Joseph R.

@JosephR .: Peut-être, mais un Schwartzian est plus flexible et fonctionne dans tous les cas. Je pense que nous pouvons avoir une autre solution en calculant la valeur hexadécimale pendant le traitement, mettra à jour ma réponse bientôt.
cuonglm

Solution cool. Je ne savais pas que ce motif avait un nom: décorer-trier-décorer. Voir mon commentaire ci-dessus.
stefan

@stefan: voir ma réponse mise à jour.
cuonglm

@Gnouc: oui, votre 2e mise à jour est définitivement qualifiée d'écriture directe. mon imagination initiale.
stefan

6

J'utilise ces données d'exemple:

1 hdh d12
2 ukr 9f
3 ezh ae
4 jjk 7
5 hhf 25

L'idée est de créer une nouvelle version de ces données avec le champ de tri sous forme décimale. C'est-à-dire awkqu'il le convertit, l'ajoute à chaque ligne, le résultat est trié et, comme dernière étape, le champ ajouté est supprimé:

awk '{val="0x" $3; sub("^0x0x","0x",val); print strtonum(val),$0 ;}' file | 
  sort -n | 
  sed 's/^[^ ]* //'

Ce qui se traduit par cette sortie:

4 jjk 7
5 hhf 25
2 ukr 9f
3 ezh ae
1 hdh d12

1
Merci, solution plutôt cool. Désolé de ne pas avoir posté mon montage plus tôt, il suit une approche similaire en utilisant couper + coller. J'espérais cependant une solution plus directe ...
Stefan

@stefan Qu'est-ce qui compte comme "direct"? La solution doit-elle être utilisée sort?
Joseph R.

@Joseph «Qu'est-ce qui compte comme« direct »?» Est la bonne question. Fondamentalement, toutes les solutions jusqu'à présent (Hauke, Gnouc ci-dessous et la mienne) font quelque chose de similaire: décodez la valeur hexadécimale, attachez le résultat aux lignes, triez-le et supprimez-le. Je cherchais quelque chose n'utilisant pas le motif décorer-trier-décorer . Les deux solutions sont supérieures aux miennes, dans la mesure où elles fonctionnent dans un pipeline. J'ai choisi celui-ci parce que je préfère personnellement utiliser awk (le plus petit marteau) que Perl pour ce genre de tâche.
stefan

J'ai déplacé mon choix de réponse au n ° 3 ci-dessous, à cause de la deuxième mise à jour de Gnouc.
stefan

1

Contribution

$ cat /tmp/input
0x45 aaa 333
0x50 dd 33
0x4 bbbb 444
0x456 cc 22
0x5 eee 1111

Tri d'une doublure

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input | sort -n -k 1 | cut -f2- -d' '
0x4 bbbb 444
0x5 eee 1111
0x45 aaa 333
0x50 dd 33
0x456 cc 22

Tri étape par étape

Étape 1: Ajoutez une nouvelle première colonne avec la représentation décimale du nombre hexadécimal.

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input 
69 0x45 aaa 333
80 0x50 dd 33
4 0x4 bbbb 444
1110 0x456 cc 22
5 0x5 eee 1111

Étape 2: Triez les lignes numériquement sur le premier champ.

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input | sort -n -k 1
4 0x4 bbbb 444
5 0x5 eee 1111
69 0x45 aaa 333
80 0x50 dd 33
1110 0x456 cc 22

Étape 3: supprimez la première colonne.

$ gawk  --non-decimal-data '{ dec = sprintf("%d", $1); print dec " "  $0 }' /tmp/input | sort -n -k 1 | cut -f2- -d' '
0x4 bbbb 444
0x5 eee 1111
0x45 aaa 333
0x50 dd 33
0x456 cc 22

0

adapté de: http://www.unix.com/302548935-post6.html?s=b4b6b3ed50b6831717f6429113302ad6

: fichier à trier:

6F993B
954B29
A23F2F
BFA91D
C68C15
8F322F
5A6D40
6D512C
9D9D63
B4B823
A0641C
A79716
A18518

Commander:

awk '{printf("%050s\t%s\n", toupper($0), $0)}' file-to-sort | LC_COLLATE=C sort -k1,1 | cut -f2

Production:

C68C15
BFA91D
B4B823
A79716
A23F2F
A18518
A0641C
9D9D63
954B29
8F322F
6F993B
6D512C
5A6D40

- où le toupper ($ 0) "met à niveau" les lettres minuscules pour qu'elles soient triées en premier (vous n'êtes pas sûr que ce soit nécessaire?)

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.