Как напечатать самую длинную строку в файле?


35

Я ищу самый простой способ напечатать самую длинную строку в файле. Я немного погуглил и неожиданно не смог найти ответ. Я часто печатаю длину самой длинной строки в файле, но я не знаю, как на самом деле напечатать самую длинную строку. Кто-нибудь может предложить решение для печати самой длинной строки в файле? Заранее спасибо.


1
Как насчет того, когда есть несколько «самых длинных» строк? Поскольку вы хотите больше, чем простая максимальная длина, хотите ли вы видеть все экземпляры линий, которые равны по длине?
— Peter.O

Ответы:


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

UPD : обобщение всех советов в комментариях

awk 'length > max_length { max_length = length; longest_line = $0 } END { print longest_line }' ./text 

3
То есть и вызов другой команды ( cat), и использование канала являются дорогостоящими операциями, не говоря уже о том, что для awk более эффективно просто читать файл. Влияние на производительность определенно заметно, если это делается часто, и даже в этом случае вы полностью злоупотребляете cat.
— Крис Даун

7
@laebshade Абсолютно есть причина - вам не нужно запоминать, какие команды принимают имена файлов, а какие нет, или заботиться о том, какая команда будет выполняться первой в конвейере. Если вы собираетесь писать сценарий, который часто запускается, непременно переживайте о чем-то подобном. Если вы пишете одноразовую вещь, чтобы найти самую длинную строку в файле, дополнительный процесс и часть времени, потраченного на это, совершенно не имеют значения. Глупо, что люди так одержимы этим, это невероятно незначительно
— Майкл Мрозек

4
@ Кит Томпсон: catздесь не бесполезно. Это может быть бесполезно для компьютера, но для человека-читателя это может обеспечить ценность. Первый вариант четко показывает входные данные. Поток более естественный (слева направо). Во втором случае вы не знаете, что это за ввод, если не прокрутите окно.
— JFS

1
@JFSebastian Даже если вы хотите это слева, вам не нужно cat. < file commandработает просто отлично.
— Крис Даун

3
@JFSebastian: тот факт, что перенаправление может быть записано в начале команды, немного неясен; < filename commandэквивалентно filename < commandв каждой оболочке, которую я пробовал. Но как только вы узнаете об этом, вы сможете воспользоваться этим при написании длинных каналов, которые четко показывают направление потока данных (без вызова дополнительной команды):< input-file command1 | command2 | command3 > output-file
— Кейт Томпсон,

6
cat filename | awk '{ print length }' | sort -n | tail -1

+1 Было много интересных решений, но это было самое простое. (Без кота было бы проще, если бы awk прочитал файл, но зачем спорить?)
— user1683793

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

Сначала он читает файл внутри подстановки команд и выводит длину самой длинной строки (ранее expandконвертирует табуляцию в пробелы, чтобы преодолеть семантику wc -L- каждая вкладка в строке будет добавлять 8 вместо 1 к длине строки). Затем эта длина используется в sedвыражении, означающем «найти строку с таким количеством символов, напечатать ее и выйти». Так что это на самом деле может быть настолько оптимальным, так как самая длинная строка находится рядом с верхом файла, хе-хе (спасибо большое за потрясающие и конструктивные комментарии).

Другой, я думал раньше, чем sed (в bash):

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

2
Этот метод очень дорогой и медленный.
— Крис Даун

2
@Chris Down: Да, это так. Но вопрос был о самом худшем, не самом эффективном методе. Тем не менее, отлично подходит для небольших и средних файлов или некритических задач.
— ата

3
ВНИМАНИЕ : опция wc -L, --max-line-lengthпечатает длину самой длинной строки, в соответствии с man-страницей, но если вы копаете глубже (например, когда вы получаете неправильные / неожиданные результаты), вы обнаружите, что эта опция увеличивает длину на 8 для каждого 1 символа табуляции \x09 посмотреть этот Unix & Linux Q / A
— Peter.O

PS. Ваш ответ напечатает все «одинаково длинные» строки, что, вероятно, является хорошей вещью ... Чтобы заставить wc считать только 1 символ на вкладке, это работает. sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
— Peter.O

1
read lineбудет интерпретировать обратные косую черту символов как буквальный полукокс, например \Aresloves к A, которые, конечно , эффективны отчеты короче , чем фактические байтовое использование ... Для того, чтобы предотвратить эту сбежавшую интерпретацию, использование: read -r line. , , , Кроме того, чтобы заставить версию sed + wc выйти после первой «самой длинной строки», измените pна {p;q}..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
— Peter.O

4

Вот решение Perl:

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

Или, если вы хотите напечатать все самые длинные строки

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

Поскольку у меня не было ничего лучше, я запустил несколько тестов для текстового файла 625M. Удивительно, но мое решение на Perl было последовательно быстрее, чем другие. Конечно, разница с принятым awkрешением крошечная, но она есть. Очевидно, что решения, которые печатают несколько строк, работают медленнее, поэтому я отсортировал их по типу, быстрее и медленнее.

Выведите только одну из самых длинных строк:

$ 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

Вывести все самые длинные строки:

$ 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

3

Grep первая самая длинная линия

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

Команда необычно трудна для чтения без практики, потому что она смешивает синтаксис оболочки и регулярных выражений.
Для объяснения я сначала буду использовать упрощенный псевдокод. Строки, начинающиеся с ##, не запускаются в оболочке.
Этот упрощенный код использует имя файла F и оставляет для удобства чтения кавычки и части регулярных выражений.

Как это работает

Команда состоит из двух частей: grep- и wcвызова:

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

wcИспользуется в расширении процесса, $( ... )так оно выполняется до grep. Он рассчитывает длину самой длинной строки. Синтаксис расширения оболочки смешивается с синтаксисом шаблона регулярного выражения в некоторой путанице, поэтому я разложу расширение процесса:

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

Здесь расширение процесса было заменено на возвращаемое значение, создавая используемую grepкомандную строку. Теперь мы можем более легко прочитать регулярное выражение: оно точно соответствует от начала ( ^) до конца ( $) строки. Выражение между ними соответствует любому символу, кроме новой строки, повторяется 42 раза. В совокупности это строки, состоящие ровно из 42 символов.


Теперь вернемся к реальным командам оболочки: grepопция -E( --extended-regexp) позволяет избежать экранирования {}. Option -m 1( --max-count=1) останавливает ее после того, как найдена первая строка. Команда <in wcзаписывает файл в его стандартный ввод, чтобы предотвратить wcпечать имени файла вместе с длиной.

Какие самые длинные строки?

Чтобы сделать примеры более удобочитаемыми, когда имя файла встречается дважды, я буду использовать переменную fдля имени файла; Каждый $fв примере может быть заменен именем файла.

f="file.txt"

Показать первую самую длинную строку - первую строку длиной до самой длинной строки:

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

Показать все самые длинные строки - все строки длиной до самой длинной строки:

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

Показать последнюю самую длинную строку - последнюю строку длиной до самой длинной строки:

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

Показать одну самую длинную строку - самая длинная строка длиннее всех других строк или ошибка:

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

(Последняя команда даже более неэффективна, чем другие, поскольку она повторяет полную команду grep. Очевидно, что она должна быть разложена таким образом, чтобы выходные данные wcи строки, записанные с помощью grep, сохранялись в переменные.
Обратите внимание, что все самые длинные строки могут фактически быть всеми строками Для сохранения в переменной необходимо сохранить только первые две строки.)


Ух ты отличный ответ, многому научился. спасибо
— кое-что

2

Следующий пример должен был быть и должен был быть комментарием к ответу dmitry.malikov , но из-за бесполезного использования видимого пространства комментариев там я решил представить его здесь, где он, по крайней мере, будет виден. ..

Это простое изменение в ДМИТРИЙ по методу AWK однопроходной.
Он печатает все «равные самые длинные» строки. (Примечание. delete arrayЯвляется расширением Gawk).

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

1

В чистом виде:

#!/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[@]}"

Как есть, код может возвращать неверные результаты. Настройка _max_line[0]=${_line}не удалит оставшуюся часть ранее накопленных более коротких «длинных линий» ... unset _max_lineочистит весь массив ...
— Peter.O

@fered Спасибо за это, был написан довольно быстро. Исправлена.
— Крис Даун

0

Я разработал небольшой скрипт для этого. Он отображает длину, номер строки и саму строку по длине, которая превышает определенный размер, например, 80 символов:

#!/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


1
Есть несколько улучшений, которые вы могли бы сделать. Цитируйте свои переменные . Это сломает любые имена файлов, которые содержат пробелы или другие странные символы. Использование $*редко хорошая идея, вы хотите"$@" . The /.*/in your awkничего не делает, так как это также соответствует пустым строкам. Вы могли бы избежать экранирования, \$0если бы вы указали одинарную кавычку 'EOF'. Зачем использовать пустой BEGIN{}блок? Наконец, вам не нужно cat, простоawk . . . "$file" | . . .
— Terdon

1
Вы также можете сделать все это в awk напрямую:awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
— terdon

-3

Вы можете использовать wc:

wc -L fileName

3
Пожалуйста, прочитайте вопрос еще раз. Требуемый вывод - самая длинная строка, а не длина самой длинной строки. Также см . Комментарий Peter.O относительно wc -Lнедостатка.
— manatwork
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.