Cách in số ký tự trong mỗi dòng của tệp văn bản


82

Tôi muốn in số ký tự trong mỗi dòng của tệp văn bản bằng lệnh unix. Tôi biết nó rất đơn giản với powershell

gc abc.txt | % {$_.length}

nhưng tôi cần lệnh unix.

Câu trả lời:


152

Sử dụng Awk.

awk '{ print length }' abc.txt

2
Đây là một số thứ tự cường độ nhanh hơn so với việc áp dụng wc -c cho mỗi dòng!
aerijman

@aerijman đối với loại vấn đề này, số lượng sáng tạo quy trình thường là yếu tố tạo ra sự khác biệt về hiệu suất nhiều nhất.
MarcH

Nếu một dòng trong tệp chứa biểu tượng cảm xúc, điều này sẽ không tạo ra độ dài như mong đợi.
user5507535

@ user5507535, nó phụ thuộc vào "độ dài" mà bạn thực sự mong đợi. Có nhiều định nghĩa có thể có cho Unicode (mawk sử dụng byte, không kiểm tra gawk).
Jan Hudec

16
while IFS= read -r line; do echo ${#line}; done < abc.txt

Nó là POSIX, vì vậy nó sẽ hoạt động ở mọi nơi.

Chỉnh sửa: Đã thêm -r theo đề xuất của William.

Chỉnh sửa: Cẩn thận với việc xử lý Unicode. Bash và zsh, với ngôn ngữ được đặt chính xác, sẽ hiển thị số điểm mã, nhưng dấu gạch ngang sẽ hiển thị byte — vì vậy bạn phải kiểm tra xem trình bao của mình hoạt động như thế nào. Và dù sao thì cũng có nhiều định nghĩa khác về độ dài trong Unicode, vì vậy nó phụ thuộc vào những gì bạn thực sự muốn.

Chỉnh sửa: Tiền tố với IFS=để tránh mất dấu cách ở đầu và cuối.


+1, nhưng ... điều này sẽ không thành công nếu đầu vào chứa '\'. Sử dụng đọc -r
William Pursell

Nếu một dòng trong tệp chứa biểu tượng cảm xúc, điều này sẽ không tạo ra độ dài như mong đợi.
user5507535

@ user5507535, thực ra, nó phụ thuộc vào "độ dài" mà bạn mong đợi. Có thể có nhiều định nghĩa cho Unicode (nhưng trong trường hợp này, các shell khác nhau thực sự sẽ làm những việc khác nhau).
Jan Hudec

Luôn đặt IFS=trên readlệnh khi muốn đọc dữ liệu tùy ý. Vì vậy IFS= read -r. readsử dụng IFSđể thực hiện tách từ và mặc dù tất cả các từ đã tách sau đó được dán lại với nhau vào một biến có sẵn ( line), không có gì đảm bảo rằng chúng sẽ được dán lại cùng với tất cả các ký tự phân tách ban đầu mà chúng có hoặc chỉ một biến có thể khác những cái. Ví dụ, với IFS mặc định, dòng foo barcó thể trở thành foo bar, mất 7 khoảng trắng. (Giống như cách Stack Overflow làm mất các khoảng trắng liền kề trong chuỗi ví dụ đó trong nhận xét này).
mtraceur

@mtraceur, tài liệu nói rõ ràng rằng "các từ còn lại và dấu phân cách xen vào của chúng được gán cho họ", vì vậy chúng sẽ được dán lại cùng với dấu phân tách ban đầu. Tuy nhiên, điều đó không quan tâm đến các dấu phân cách đầucuối , chúng thực sự bị mất. Vì vậy, bạn đúng, IFSnên được đặt ra, nhưng vấn đề khi nó không tinh tế hơn.
Jan Hudec

4

Tôi đã thử các câu trả lời khác được liệt kê ở trên, nhưng chúng vẫn còn rất xa so với các giải pháp tốt khi xử lý các tệp lớn - đặc biệt là khi kích thước của một dòng chiếm hơn ~ 1/4 RAM khả dụng.

Cả bash và awk slurp toàn bộ dòng, mặc dù đối với vấn đề này, nó không cần thiết. Bash sẽ xuất hiện lỗi khi một dòng quá dài, ngay cả khi bạn có đủ bộ nhớ.

Tôi đã triển khai một tập lệnh python cực kỳ đơn giản, khá không được tối ưu hóa mà khi được thử nghiệm với các tệp lớn (~ 4 GB mỗi dòng) không bị trượt và cho đến nay là một giải pháp tốt hơn những giải pháp được đưa ra.

Nếu đây là mã thời gian quan trọng để sản xuất, bạn có thể viết lại các ý tưởng trong C hoặc thực hiện tối ưu hóa tốt hơn trên lệnh gọi đọc (thay vì chỉ đọc một byte duy nhất tại một thời điểm), sau khi kiểm tra rằng đây thực sự là một nút cổ chai.

Mã giả định dòng mới là một ký tự dòng cấp, đây là một giả định tốt cho Unix, nhưng YMMV trên Mac OS / Windows. Đảm bảo tệp kết thúc bằng một dòng cấp dữ liệu để đảm bảo số lượng ký tự dòng cuối cùng không bị bỏ sót.

from sys import stdin, exit

counter = 0
while True:
    byte = stdin.buffer.read(1)
    counter += 1
    if not byte:
        exit()
    if byte == b'\x0a':
        print(counter-1)
        counter = 0

1
Câu hỏi dành cho một tệp "văn bản". Tôi không nghĩ 4GB mỗi dòng phù hợp với bất kỳ định nghĩa hợp lý nào về tệp văn bản.
MarcH

3

Đây là ví dụ sử dụng xargs:

$ xargs -d '\n' -I% sh -c 'echo % | wc -c' < file

"Echo%" này không xử lý các ký tự không an toàn cần trích dẫn từ shell. Ngoài ra, "xargs" sẽ chia nhỏ tệp của bạn theo khoảng trắng và dòng mới, không chỉ dòng mới như người đăng ban đầu yêu cầu.

1

Thử đi:

while read line    
do    
    echo -e |wc -m      
done <abc.txt    

Ý bạn là echo -e | wc -m, phải không? Việc sử dụng các lệnh là vô ích; shell có thể đếm các ký tự trong một biến. Plus echo -ehoàn toàn không tương thích và hoạt động ở một nửa số shell trong khi bắt đầu với một số trình tự thoát hoạt động ở một số khác và không có gì trong phần còn lại.
Jan Hudec
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.