Làm thế nào để in dòng dài nhất trong một tập tin?


35

Tôi đang tìm phương pháp đơn giản nhất để in dòng dài nhất trong một tệp. Tôi đã làm một số việc và dường như không thể tìm thấy câu trả lời. Tôi thường in độ dài của dòng dài nhất trong một tệp, nhưng tôi không biết làm thế nào để thực sự in dòng dài nhất. Bất cứ ai cũng có thể cung cấp một giải pháp để in dòng dài nhất trong một tập tin? Cảm ơn trước.


1
Còn khi có nhiều dòng "dài nhất" thì sao ?. Bởi vì bạn muốn nhiều hơn một độ dài tối đa đơn giản, bạn có muốn xem tất cả các trường hợp của các dòng dài nhất bằng nhau không?
Peter.O

Câu trả lời:


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

CẬP NHẬT : tóm tắt tất cả các lời khuyên trong các ý kiến

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

3
Đó là, cả việc gọi một lệnh khác ( cat) và sử dụng một đường ống là các hoạt động đắt tiền, chưa kể rằng nó hiệu quả hơn cho awk khi chỉ đọc tệp. Ý nghĩa về hiệu suất chắc chắn là đáng chú ý nếu điều này được thực hiện thường xuyên và thậm chí như vậy, bạn hoàn toàn sử dụng sai cat.
Chris Xuống

7
@laebshade Hoàn toàn có một lý do - vì vậy bạn không cần phải nhớ lệnh nào lấy tên tệp và không, hoặc quan tâm đến lệnh nào sẽ được thực thi trước trong đường ống. Nếu bạn sẽ viết một kịch bản được chạy thường xuyên, bằng mọi cách có thể lo lắng về điều gì đó như thế này. Nếu bạn đang viết một thứ duy nhất để tìm dòng dài nhất trong một tệp, thì quá trình bổ sung và lượng thời gian phân đoạn là hoàn toàn không liên quan. Thật ngớ ngẩn khi mọi người bị ám ảnh bởi nó ở đây, nó cực kỳ nhỏ
Michael Mrozek

4
@Keith Thompson: catkhông phải là vô dụng ở đây. Nó có thể vô dụng với máy tính nhưng đối với người đọc thì nó có thể cung cấp giá trị. Các biến thể đầu tiên rõ ràng cho thấy đầu vào. Dòng chảy tự nhiên hơn (từ trái sang phải). Trong trường hợp thứ hai, bạn không biết đầu vào là gì trừ khi bạn cuộn cửa sổ.
jfs

1
@JFSebastian Ngay cả khi bạn muốn nó ở bên trái, bạn không cần cat. < file commandhoạt động tốt
Chris Xuống

3
@JFSebastian: Việc chuyển hướng có thể được viết khi bắt đầu lệnh có phần mơ hồ; < filename commandtương đương với filename < commandmọi vỏ tôi đã thử. Nhưng một khi bạn nhận thức được nó, bạn có thể tận dụng lợi thế của nó khi viết các ống dài thể hiện rõ hướng của luồng dữ liệu (mà không cần gọi thêm lệnh):< input-file command1 | command2 | command3 > output-file
Keith Thompson

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

+1 Có rất nhiều giải pháp thú vị cho việc này nhưng đây là cách đơn giản nhất. (Sẽ đơn giản hơn nếu không có con mèo bằng cách cho awk đọc tệp nhưng tại sao lại ngụy biện?)
user1683793

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

Điều này trước tiên đọc tệp bên trong thay thế lệnh và xuất độ dài của dòng dài nhất, (trước đó, expandchuyển đổi các tab thành khoảng trắng, để vượt qua ngữ nghĩa của wc -L- mỗi tab trong dòng sẽ thêm 8 thay vì 1 cho chiều dài dòng). Độ dài này sau đó được sử dụng trong một sedbiểu thức có nghĩa là "tìm một dòng số ký tự này dài, in nó, sau đó thoát". Vì vậy, điều này thực sự có thể là tối ưu vì dòng dài nhất nằm gần đầu tệp, heheh (cảm ơn vì đã nhận xét tuyệt vời và mang tính xây dựng).

Một cái khác, tôi đã nghĩ sớm hơn cái sed (trong bash):

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

2
Phương pháp này rất tốn kém và chậm.
Chris Xuống

2
@Chris Xuống: Ồ đúng rồi. Nhưng câu hỏi là về phương pháp sắp xếp nhất, không hiệu quả nhất. Mặc dù vậy, tốt cho các tệp nhỏ đến trung bình hoặc các tác vụ không quan trọng.
ata

3
CẢNH BÁO : tùy chọn của wc -L, --max-line-lengthin độ dài của dòng dài nhất, theo trang man, nhưng nếu bạn đào sâu hơn (như khi bạn nhận được kết quả sai / không mong muốn ), bạn thấy rằng tùy chọn này tăng chiều dài thêm 8 cho mỗi 1 tab char \x09 xem Q / A Unix & Linux này
Peter.O

Tái bút Câu trả lời của bạn sẽ in tất cả các dòng "dài bằng nhau", có lẽ là một điều tốt ... Để buộc wc chỉ đếm 1 char mỗi tab, điều này hoạt động. sed -rn "/.{$(<file expand -t1 |wc -L)}/p" file
Peter.O

1
read linesẽ diễn giải các ký tự thoát dấu gạch chéo ngược như char theo nghĩa đen, ví dụ như \Anối lại A, tất nhiên báo cáo hiệu quả ngắn hơn mức sử dụng byte thực tế ... Để ngăn cách giải nghĩa thoát này , hãy sử dụng : read -r line. . . . Ngoài ra, để làm cho phiên bản sed + wc thoát ra sau "dòng dài nhất" đầu tiên, hãy đổi pthành {p;q}..sed -rn "/.{$(<file expand -t1 |wc -L)}/{p;q}" file
Peter.O

4

Đây là một giải pháp Perl:

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

Hoặc, nếu bạn muốn in tất cả các dòng dài nhất

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

Vì tôi không có gì tốt hơn để làm, tôi đã chạy một số điểm chuẩn trên tệp văn bản 625M. Đáng ngạc nhiên, giải pháp Perl của tôi luôn nhanh hơn các giải pháp khác. Cấp, sự khác biệt với các awkgiải pháp được chấp nhận là rất nhỏ, nhưng nó là ở đó. Rõ ràng, các giải pháp in nhiều dòng chậm hơn nên tôi đã sắp xếp theo loại, nhanh nhất đến chậm nhất.

Chỉ in một trong những dòng dài nhất:

$ 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

In tất cả các dòng dài nhất:

$ 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 dòng dài nhất đầu tiên

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

Lệnh khó đọc một cách bất thường mà không cần thực hành vì nó trộn cú pháp shell- và regrec.
Để giải thích, tôi sẽ sử dụng mã giả đơn giản hóa trước. Các dòng bắt đầu ##không chạy trong vỏ.
Mã đơn giản hóa này sử dụng tên tệp F và bỏ qua phần trích dẫn và các phần của biểu thức chính quy để dễ đọc.

Làm thế nào nó hoạt động

Lệnh này có hai phần, một grep- và một lệnh wcgọi:

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

wcđược sử dụng trong một quá trình mở rộng $( ... ), vì vậy nó được chạy trước đó grep. Nó tính toán độ dài của dòng dài nhất. Cú pháp mở rộng shell được trộn với cú pháp mẫu biểu thức chính quy theo cách khó hiểu, vì vậy tôi sẽ phân tách quá trình mở rộng quy trình:

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

Ở đây, quá trình mở rộng đã được thay thế bằng giá trị mà nó sẽ trả về, tạo ra grepdòng lệnh được sử dụng. Bây giờ chúng ta có thể đọc biểu thức chính quy dễ dàng hơn: Nó khớp chính xác từ start ( ^) đến end ( $) của dòng. Biểu thức giữa chúng khớp với bất kỳ ký tự nào ngoại trừ dòng mới, được lặp lại 42 lần. Kết hợp lại, đó là các dòng bao gồm chính xác 42 ký tự.


Bây giờ, quay lại các lệnh shell thực: grepTùy chọn -E( --extended-regexp) cho phép không thoát khỏi {}khả năng đọc. Tùy chọn -m 1( --max-count=1) làm cho nó dừng lại sau khi dòng đầu tiên được tìm thấy. Lệnh <trong wcghi ghi tệp vào stdin của nó, để ngăn không cho wcin tên tệp cùng với độ dài.

Dòng nào dài nhất?

Để làm cho các ví dụ dễ đọc hơn với tên tệp xảy ra hai lần, tôi sẽ sử dụng một biến fcho tên tệp; Mỗi $ftrong ví dụ có thể được thay thế bằng tên tệp.

f="file.txt"

Hiển thị dòng dài nhất đầu tiên - dòng đầu tiên dài bằng dòng dài nhất:

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

Hiển thị tất cả các dòng dài nhất - tất cả các dòng dài bằng dòng dài nhất:

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

Hiển thị dòng dài nhất cuối cùng - dòng cuối cùng dài bằng dòng dài nhất:

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

Hiển thị dòng dài nhất - dòng dài nhất dài hơn tất cả các dòng khác hoặc không thành công:

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

(Lệnh cuối cùng thậm chí còn kém hiệu quả hơn các lệnh khác, vì nó lặp lại lệnh grep hoàn chỉnh. Rõ ràng nó phải được phân tách để đầu ra wcvà các dòng được viết bởi grepcác biến.
Lưu ý rằng tất cả các dòng dài nhất có thể thực sự là tất cả các dòng . Để lưu trong một biến, chỉ cần giữ hai dòng đầu tiên.)


Wow câu trả lời tuyệt vời, học được rất nhiều từ nó. cảm ơn
vài thứ gì đó

2

Ví dụ sau đây sẽ là, và đáng lẽ phải là một bình luận cho câu trả lời của dmitry.malikov , nhưng vì sử dụng Không gian bình luận hữu hình vô dụng ở đó, tôi đã chọn trình bày nó ở đây, nơi ít nhất nó sẽ được nhìn thấy. ..

Đây là một biến thể đơn giản của phương pháp awk pass-pass đơn của dmitry .
Nó in tất cả các dòng "dài nhất bằng nhau". (Lưu ý. delete arrayLà một phần mở rộng gawk).

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

1

Trong bash tinh khiết:

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

Như vậy, mã có thể trả về kết quả không hợp lệ. Cài đặt _max_line[0]=${_line}không xóa phần còn lại của bất kỳ "dòng dài nhất" nào được tích lũy trước đó ... unset _max_linesẽ xóa toàn bộ mảng ...
Peter.O

@fered Cảm ơn vì điều đó, đã được viết khá nhanh. Đã sửa.
Chris Xuống

0

Tôi đã phát triển một kịch bản shell nhỏ cho việc này. Nó hiển thị độ dài, dòng # và chính dòng theo độ dài vượt quá kích thước cụ thể như 80 ký tự:

#!/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
Có một vài cải tiến bạn có thể thực hiện. Trích dẫn các biến của bạn . Điều này sẽ phá vỡ bất kỳ tên tệp có chứa khoảng trắng hoặc các ký tự lạ khác. Sử dụng $*hiếm khi là một ý tưởng tốt, bạn muốn"$@" . Các /.*/tại của bạn awkkhông làm gì cả vì đó phù hợp với dòng sản phẩm nào là tốt. Bạn có thể tránh thoát \$0nếu bạn trích dẫn 'EOF'. Tại sao sử dụng một BEGIN{}khối trống ? Cuối cùng, bạn không cần cat, chỉ cầnawk . . . "$file" | . . .
terdon

1
Bạn cũng có thể làm tất cả mọi việc trong awk trực tiếp:awk -vmax=15 '{len=length($0); if(len>=max){printf("%s, %d at line # %d %s\n", FILENAME, len, NR, $0);}}' file*
terdon

-3

Bạn có thể sử dụng wc:

wc -L fileName

3
Xin vui lòng đọc lại câu hỏi. Đầu ra cần thiết là chính dòng dài nhất, không phải là độ dài của dòng dài nhất. Cũng xem bình luận của Peter.O về wc -Lnhược điểm của.
manatwork
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.