Phương pháp nhanh chóng tách chuỗi từ textfile?


11

Tôi có hai tệp văn bản: string.txt và lengths.txt

Chuỗi.txt:

abcdefghijklmnopqrstuvwxyz

lengths.txt

5
4
10
7

Tôi muốn lấy tập tin

>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

Tôi đang làm việc với khoảng 28.000 mục và chúng khác nhau giữa 200 và 56.000 ký tự.

Hiện tại, tôi đang sử dụng:

start=1
end=0
i=0
while read read_l
do
    let i=i+1
    let end=end+read_l
    echo -e ">Entry_$i" >>outfile.txt
    echo "$(cut -c$start-$end String.txt)" >>outfile.txt
    let start=start+read_l
    echo $i
done <lengths.txt

Nhưng nó rất không hiệu quả. Còn ý tưởng nào hay hơn không?


Làm thế nào về str="$(cat string.txt)"; i=0; while read j; do echo "${file:$i:$j}"; i=$((i+j)); done <length.txt.. đủ nhanh như được thực hiện chỉ bằng vỏ ..
heemayl

Thành thật mà nói không nhanh hơn nhiều. Nó vẫn còn khá lâu. Tôi còn khá mới mẻ với linux / lập trình nên nếu bạn nghĩ rằng có một phương pháp nhanh hơn không chỉ sử dụng shell, tôi sẽ mở ra cho các ý tưởng.
dùng3891532

4
Hãy thử { while read l<&3; do head -c"$l"; echo; done 3<lengths.txt; } <String.txt.
jimmij

@jimmij, làm thế nào về việc gắn nó vào câu trả lời
iruvar

Câu trả lời:


7

Bạn có thể làm

{
  while read l<&3; do
    {
      head -c"$l"
      echo
    } 3<&-
  done 3<lengths.txt
} <String.txt

Nó đòi hỏi một số lời giải thích:

Ý tưởng chính là sử dụng { head ; } <filevà được lấy từ câu trả lời bị đánh giá thấp @mikeerv . Tuy nhiên, trong trường hợp này, chúng tôi cần sử dụng nhiều heads, do đó whilevòng lặp được giới thiệu và một chút điều chỉnh với các mô tả tệp để chuyển đến headđầu vào từ cả hai tệp (tệp String.txtdưới dạng tệp chính để xử lý và chuyển từ length.txtlàm đối số sang -ctùy chọn) . Ý tưởng là lợi ích về tốc độ nên đến từ việc không cần tìm kiếm qua String.txtmỗi lần một lệnh giống như headhoặc cutđược gọi. Đây echochỉ là để in dòng mới sau mỗi lần lặp.

Bao nhiêu là nhanh hơn (nếu có) và thêm >Entry_igiữa các dòng được để lại như một bài tập.


Sử dụng gọn gàng chuyển hướng I / O. Vì thẻ là Linux, nên bạn có thể giả sử vỏ một cách hợp lý là Bash và sử dụng read -u 3để đọc từ mô tả 3.
Jonathan Leffler

@JonathanLeffler, Linux không có gì phải làm bash. Phần lớn các hệ thống dựa trên Linux không bashđược cài đặt (nghĩ rằng Android và các hệ thống nhúng khác). bashlà vỏ chậm nhất của tất cả, chuyển sang bash sẽ biểu diễn khả năng suy thoái nhiều hơn đáng kể so với lợi ích nhỏ mà chuyển từ read <&3để read -u3có thể mang lại (mà trong trường hợp bất kỳ sẽ không đáng kể so với chi phí điều hành một lệnh bên ngoài như head). Chuyển sang ksh93 đã tích hợp headsẵn (và một hỗ trợ -ctùy chọn không chuẩn ) sẽ cải thiện hiệu suất hơn rất nhiều.
Stéphane Chazelas

Lưu ý rằng đối số của head -c(đối với việc headtriển khai có sẵn tùy chọn không chuẩn) là một số byte, không phải ký tự. Điều đó sẽ tạo ra sự khác biệt trong các địa phương nhiều byte.
Stéphane Chazelas

7

Nói chung, bạn không muốn sử dụng các vòng lặp shell để xử lý văn bản . Ở đây, tôi sẽ sử dụng perl:

$ perl -lpe 'read STDIN,$_,$_; print ">Entry_" . ++$n' lengths.txt < string.txt
>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

Đó là một lệnh, đọc (với bộ đệm rất hiệu quả hơn readlệnh của shell đọc một byte (hoặc một vài byte cho các tệp thông thường) tại một thời điểm) cả hai tệp chỉ một lần (mà không lưu trữ chúng đầy đủ trong bộ nhớ), vì vậy sẽ có một số đơn đặt hàng có cường độ hiệu quả hơn các giải pháp chạy các lệnh bên ngoài trong một vòng lặp shell.

(thêm -Ctùy chọn nếu các số đó phải là số ký tự trong ngôn ngữ hiện tại trái ngược với số byte. Đối với các ký tự ASCII như trong mẫu của bạn, điều đó sẽ không tạo ra bất kỳ sự khác biệt nào).


Đó là một sự tái sử dụng phức tạp của $_cả tham số đầu ra và đầu vào read, nhưng nó làm giảm số byte trong tập lệnh.
Jonathan Leffler

Trong một thử nghiệm nhanh (mẫu của OP được lặp lại 100000 lần), tôi thấy giải pháp này nhanh gấp khoảng 1200 lần so với @ jimmij's (0,3 giây so với 6 phút (với bash, 16 giây với PATH=/opt/ast/bin:$PATH ksh93)).
Stéphane Chazelas

6

bash, phiên bản 4

mapfile -t lengths <lengths.txt
string=$(< String.txt)
i=0 
n=0
for len in "${lengths[@]}"; do
    echo ">Entry_$((++n))"
    echo "${string:i:len}"
    ((i+=len))
done

đầu ra

>Entry_1
abcde
>Entry_2
fghi
>Entry_3
jklmnopqrs
>Entry_4
tuvwxyz

4

Thế còn awk?

Tạo một tệp được gọi process.awkvới mã này:

function idx(i1, v1, i2, v2)
{
     # numerical index comparison, ascending order
     return (i1 - i2)
}
FNR==NR { a[FNR]=$0; next }
{ i=1;PROCINFO["sorted_in"] = "idx";
        for (j in a) {
                print ">Entry"j;
                ms=substr($0, i,a[j])
                print ms
                i=i+length(ms)
        }
}

Lưu nó và thực hiện awk -f process.awk lengths.txt string.txt


Dựa trên việc sử dụng PROCINFO, đây không phải là tiêu chuẩn awk, nhưng gawk. Trong trường hợp đó, tôi thích một gawktính năng duy nhất khác, đó là FIELDWIDTHS:awk -vFIELDWIDTHS="$(tr '\n' ' ' < lengths.txt)" '{for(i=1;i<=NF;i++)print">Entry"i ORS$i}' string.txt
thao tác
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.