web-dev-qa-db-fra.com

Comment imprimer la ligne la plus longue dans un fichier?

Je cherche la méthode la plus simple pour imprimer la ligne la plus longue d'un fichier. J'ai fait des googles et étonnamment ne pouvais pas sembler trouver une réponse. J'imprimais fréquemment la longueur de la ligne la plus longue d'un fichier, mais je ne sais pas comment imprimer réellement la ligne la plus longue. Quelqu'un peut-il fournir une solution pour imprimer la ligne la plus longue d'un fichier? Merci d'avance.

37
dr.bunsen
cat ./text | awk ' { if ( length > x ) { x = length; y = $0 } }END{ print y }'

UPD : résumer tous les conseils dans les commentaires

awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text 
cat filename | awk '{ print length }' | sort -n | tail -1
7
aspinalln

Grep la première ligne la plus longue

grep -Em1 "^.{$(wc -L <file.txt)}\$" file.txt 

La commande est inhabituellement difficile à lire sans pratique car elle mélange la syntaxe de la coquille et de la réégycle.
[.____] Pour une explication, j'utiliserai d'abord un pseudocode simplifié. Les lignes commençant par ## ne fonctionnent pas dans le Shell.
Ce code simplifié utilise le nom de fichier F, et quitte la citation et des parties de Regexps pour la lisibilité.

Comment ça fonctionne

La commande comporte deux parties, une grep- et une invocation wc:

## grep "^.{$( wc -L F )}$" F

Le wc est utilisé dans une extension du procédé, $( ... ), de sorte qu'il est exécuté avant grep. Il calcule la longueur de la ligne la plus longue. La syntaxe de dilatation de coquille est mélangée à la syntaxe de modèle d'expression régulière de manière déroutante, alors je vais décomposer l'expansion du processus:

## wc -L F
42
## grep "^.{42}$" F

Ici, l'expansion du processus a été remplacée par la valeur qu'il reviendrait, en créant la ligne de commande grep qui est utilisé. Nous pouvons maintenant lire l'expression régulière plus facilement: Il correspond exactement du début (^) à la fin ($) de la ligne. L'expression entre eux correspond à n'importe quel caractère sauf nouvelle ligne, répétée de 42 fois. Combiné, c'est-à-dire des lignes comportant exactement 42 caractères.


Maintenant, retour à de vraies commandes Shell: L'option grep-E (--extended-regexp) permet de ne pas échapper à la {} pour une meilleure lisibilité. Option -m 1 (--max-count=1) fait arrêter après la première ligne se trouve. La < dans la commande wc écrit le fichier à son stdin, pour empêcher wc d'imprimer le nom du fichier ainsi que la longueur.

Quelles lignes les plus longues?

Pour rendre les exemples plus lisible avec le nom de fichier se produit deux fois, je vais utiliser une f variable pour le nom du fichier; Chaque $f dans l'exemple pourrait être remplacé par le nom du fichier.

f="file.txt"

Montrer la première ligne la plus longue - la première ligne qui est aussi longue que la ligne la plus longue:

grep -E -m1 "^.{$(wc -L <"$f")}\$" "$f"

Show toutes les lignes les plus longues - Toutes les lignes qui sont aussi longues que la ligne la plus longue:

grep -E "^.{$(wc -L <"$f")}\$" "$f" 

Afficher la dernière ligne la plus longue - La dernière ligne qui est aussi longue que la ligne la plus longue:

tac "$f" | grep -E -m1 "^.{$(wc -L <"$f")}\$"

Afficher la (ligne la plus longue - la ligne la plus longue plus longue que toutes les autres lignes ou échouer:

[ $(grep -E "^.{$(wc -L <"$f")}\$" "$f" | wc -l) = 1 ] && grep -E "^.{$(wc -L <"$f")}\$" "$f" 

(La dernière commande est encore plus inefficace que les autres, car il répète la commande complète grep. Il doit évidemment être décomposé de telle sorte que la sortie de wc et les lignes écrites par grep sont enregistrées dans les variables.
[.____] Notez que toutes les lignes les plus longues peuvent en réalité être toutes lignes. Pour économiser dans une variable, seules les deux premières lignes doivent être conservées.)

5
Volker Siegel
sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file

Ceci lit d'abord le fichier à l'intérieur de la substitution de commande et génère la longueur de la ligne la plus longue, (précédemment, expand convertit les onglets en espaces, pour surmonter la sémantique de wc -L - Chaque onglet dans la ligne ajoutera 8 au lieu de 1 à la longueur de la ligne). Cette longueur est ensuite utilisée dans une signification d'expression sed "Trouvez une ligne de ce nombre de caractères longtemps, d'imprimer, puis de quitter". Cela peut donc être aussi optimal que la ligne la plus longue à proximité du sommet du fichier, heheh (merci pour les commentaires géniaux et constructifs).

Un autre, j'avais pensé plus tôt que le SED (en bash):

#!/bin/bash
while read -r line; do
    (( ${#line} > max )) && max=${#line} && longest="$line"
done
echo "$longest"
5
ata

Voici une solution PERL:

Perl -e 'while(<>){
           $l=length;  
           $l>$m && do {$c=$_; $m=$l}  
         } print $c' file.txt 

Ou, si vous voulez imprimer tous les lignes les plus longues

Perl -e 'while(<>){
           $l=length;
           Push @{$k{$l}},$_;
           $m=$l if $l>$m;
         } print @{$k{$m}}' file.txt 

Depuis que je n'avais rien de mieux à faire, j'ai couru des points de repère sur un fichier texte de 625 m. Étonnamment, ma solution Perl était toujours plus rapide que les autres. Certes, la différence avec la solution acceptée awk est minuscule, mais elle est là. De toute évidence, des solutions qui impriment plusieurs lignes sont plus lentes, donc j'ai trié par type, le plus rapide au plus lent.

Imprimez une seule des lignes les plus longues:

$ time Perl -e 'while(<>){
           $l=length;  
           $l>$m && do {$c=$_; $m=$l}  
         } print $c' file.txt 
real    0m3.837s
user    0m3.724s
sys     0m0.096s



$ time awk 'length > max_length { max_length = length; longest_line = $0 }
 END { print longest_line }' file.txt
real    0m5.835s
user    0m5.604s
sys     0m0.204s



$ time sed -rn "/.{$(<file.txt expand -t1 |wc -L)}/{p;q}" file.txt 
real    2m37.348s
user    2m39.990s
sys     0m1.868s

Imprimer toutes les lignes les plus longues:

$ time Perl -e 'while(<>){
           $l=length;
           Push @{$k{$l}},$_;
           $m=$l if $l>$m;
         } print @{$k{$m}}' file.txt 
real    0m9.263s
user    0m8.417s
sys     0m0.760s


$ time awk 'length >x { delete y; x=length }
     length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file.txt
real    0m10.220s
user    0m9.925s
sys     0m0.252s


## This is Chris Down's bash solution
$ time ./a.sh < file.txt 
Max line length: 254
Lines matched with that length: 2
real    8m36.975s
user    8m17.495s
sys     0m17.153s
4
terdon

L'exemple suivant allait être, et aurait dû être un commentaire à Dmitry.malikov's Réponse, mais à cause de la tilisation inutile de l'espace de commentaire visible là-bas, j'ai Choisi pour le présenter ici, où il sera au moins vu ...

Ceci est une simple variation de la méthode Dmitry's Single Pass Awk.
[.____] Il imprime toutes les lignes "égales plus longues". (Noter. delete array est une extension GAWK).

awk 'length >x { delete y; x=length }
     length==x { y[NR]=$0 } END{ for (z in y) print y[z] }' file
2
Peter.O

Pure Bash:

#!/bin/bash

_max_length=0
while IFS= read -r _line; do
    _length="${#_line}"
    if (( _length > _max_length )); then
        _max_length=${_length}
        _max_line=( "${_line}" )
    Elif (( _length == _max_length )); then
        _max_line+=( "${_line}" )
    fi
done

printf 'Max line length: %d\n' "${_max_length}"
printf 'Lines matched with that length: %d\n' "${#_max_line[@]}"
(( ${#_max_line[@]} )) && printf '%s\n' '----------------' "${_max_line[@]}"
1
Chris Down

J'ai développé un petit script shell pour cela. Il affiche la longueur, la ligne de ligne et la ligne elle-même de la longueur dépasse une taille particulière comme 80 caractères:

#!/bin/sh

# Author: Surinder

if test $# -lt 2
then
   echo "usage: $0 length file1 file2 ..."
   echo "usage: $0 80 hello.c"
   exit 1
fi

length=$1

shift

LONGLINE=/tmp/longest-line-$$.awk

cat << EOF > $LONGLINE
  BEGIN {
  }

  /.*/ {
    current_length=length(\$0);
    if (current_length >= expected_length) {
       printf("%d at line # %d %s\n", current_length, NR, \$0);
    }
  }

  END {
  }
EOF

for file in $*
do
  echo "$file"
  cat $file | awk -v expected_length=$length -f $LONGLINE |sort -nr
done

rm $LONGLINE

https://github.com/lordofrain/tools/blob/master/longest-line/longest-line.sh

0
Surinder432