วิธีแยกสตริงจาก textfile อย่างรวดเร็ว?


11

ฉันมีไฟล์ข้อความสองไฟล์: string.txt และ lengths.txt

String.txt:

abcdefghijklmnopqrstuvwxyz

lengths.txt

5
4
10
7

ฉันต้องการรับไฟล์

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

ฉันทำงานกับ 28,000 รายการและพวกเขาแตกต่างกันระหว่าง 200 และ 56,000 ตัวอักษร

ในขณะนี้ฉันกำลังใช้:

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

แต่มันไม่มีประสิทธิภาพมาก มีความคิดที่ดีกว่านี้ไหม?


วิธีการเกี่ยวกับstr="$(cat string.txt)"; i=0; while read j; do echo "${file:$i:$j}"; i=$((i+j)); done <length.txt..seems รวดเร็วพอทำโดยเฉพาะเปลือก ..
heemayl

มันไม่เร็วเท่าไหร่ที่จะซื่อสัตย์ มันยังคงใช้เวลาค่อนข้างนาน ฉันค่อนข้างใหม่สำหรับ linux / programming ดังนั้นถ้าคุณคิดว่ามีวิธีที่เร็วกว่าไม่เพียง แต่ใช้ shell ฉันเปิดรับความคิด
user3891532

4
ลอง{ while read l<&3; do head -c"$l"; echo; done 3<lengths.txt; } <String.txtดู
jimmij

@jimmij, วิธีการเกี่ยวกับการติดที่เป็นคำตอบ
iruvar

คำตอบ:


7

คุณทำได้

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

มันต้องการคำอธิบายบางอย่าง:

แนวคิดหลักคือการใช้งาน{ head ; } <fileและมาจาก @mikeserv ประเมินคำตอบ อย่างไรก็ตามในกรณีนี้เราจำเป็นต้องใช้จำนวนมากheadดังนั้นจึงwhileมีการแนะนำ loop และ tweaking เล็กน้อยกับ file descriptors เพื่อส่งผ่านไปยังheadอินพุตจากไฟล์ทั้งสอง (ไฟล์String.txtเป็นไฟล์หลักเพื่อประมวลผลและบรรทัดจากlength.txtเป็น-cตัวเลือกอาร์กิวเมนต์) . แนวคิดคือประโยชน์ของความเร็วควรมาจากการไม่ต้องค้นหาตลอดString.txtเวลาที่คำสั่งเหมือนheadหรือcutถูกเรียกใช้ นี่echoเป็นเพียงการพิมพ์บรรทัดใหม่หลังจากทำซ้ำแต่ละครั้ง

มันจะเร็วเท่าไหร่ (ถ้ามี) และการเพิ่ม>Entry_iระหว่างบรรทัดที่เหลือเป็นแบบฝึกหัด


ใช้การเปลี่ยนเส้นทาง I / O อย่างเรียบร้อย เนื่องจากแท็กคือ Linux คุณสามารถสันนิษฐานได้เลยว่าเชลล์คือ Bash และใช้read -u 3เพื่ออ่านจาก descriptor 3
Jonathan Leffler

@JonathanLeffler bashลินุกซ์มีน้อยจะทำอย่างไรกับ ระบบที่ใช้ Linux ส่วนใหญ่ไม่ได้bashติดตั้ง (คิดว่า Android และระบบฝังตัวอื่น ๆ ) bashเป็นเปลือกช้าที่สุดของทั้งหมดเปลี่ยนไปทุบตีจะมีแนวโน้มผลการดำเนินงานที่ทำให้เสื่อมเสียมากขึ้นอย่างมีนัยสำคัญกว่ากำไรเล็ก ๆ น้อย ๆ ที่เปลี่ยนจากread <&3การread -u3อาจนำ (ซึ่งในกรณีใด ๆ จะไม่มีนัยสำคัญเมื่อเทียบกับค่าใช้จ่ายของการใช้คำสั่งภายนอกเช่นhead) การสลับไปใช้ ksh93 ที่มีในheadตัว (และอีกอันหนึ่งที่รองรับ-cตัวเลือกที่ไม่ได้มาตรฐาน) จะช่วยปรับปรุงการแสดงได้มากขึ้น
Stéphane Chazelas

โปรดทราบว่าอาร์กิวเมนต์ของhead -c(สำหรับheadการนำไปใช้งานที่มีตัวเลือกที่ไม่ได้มาตรฐาน) คือจำนวนไบต์ไม่ใช่ตัวอักษร ซึ่งจะสร้างความแตกต่างในโลแคลหลายไบต์
Stéphane Chazelas

7

โดยทั่วไปคุณไม่ต้องการที่จะใช้ลูปเชลล์ข้อความกระบวนการ ที่นี่ฉันจะใช้perl:

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

นั่นคือคำสั่งเดียวที่อ่าน (พร้อมบัฟเฟอร์ดังนั้นมีประสิทธิภาพมากกว่าreadคำสั่งเชลล์ที่อ่านหนึ่งไบต์ (หรือไม่กี่ไบต์สำหรับไฟล์ปกติ) ต่อครั้ง) ทั้งสองไฟล์เพียงครั้งเดียว (โดยไม่เก็บไว้ในหน่วยความจำเต็ม) ดังนั้นจึงเป็น จะมีขนาดของใบสั่งที่มีประสิทธิภาพมากกว่าโซลูชันที่รันคำสั่งภายนอกในวงวน

(เพิ่ม-Cตัวเลือกหากตัวเลขเหล่านั้นควรเป็นจำนวนอักขระในสถานที่ปัจจุบันเทียบกับจำนวนไบต์สำหรับอักขระ ASCII เช่นในตัวอย่างของคุณซึ่งจะไม่สร้างความแตกต่างใด ๆ )


เป็นการใช้ซ้ำอย่างซับซ้อน$_ทั้งเอาต์พุตและพารามิเตอร์อินพุตถึงreadแต่จะลดจำนวนไบต์ในสคริปต์
Jonathan Leffler

ในการทดสอบอย่างรวดเร็ว (ตัวอย่างของ OP ทำซ้ำ 100,000 ครั้ง) ฉันพบว่าวิธีแก้ปัญหานี้เร็วกว่า 1200 เท่าของ@ jimmij (0.3 วินาทีเทียบกับ 6 นาที (กับbash16 วินาทีด้วยPATH=/opt/ast/bin:$PATH ksh93))
Stéphane Chazelas

6

ทุบตีรุ่น 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

เอาท์พุต

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

4

เกี่ยวกับawkอะไร

สร้างไฟล์ที่เรียกว่าprocess.awkด้วยรหัสนี้:

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)
        }
}

บันทึกและดำเนินการ awk -f process.awk lengths.txt string.txt


ขึ้นอยู่กับการใช้งานของPROCINFOนี้ไม่ได้มาตรฐานแต่awk gawkในกรณีที่ฉันต้องการgawkคุณสมบัติอื่นเท่านั้นFIELDWIDTHS:awk -vFIELDWIDTHS="$(tr '\n' ' ' < lengths.txt)" '{for(i=1;i<=NF;i++)print">Entry"i ORS$i}' string.txt
จัดการ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.