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


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.