Dize textfile hızlı bölme yöntemi?


11

İki metin dosyam var: string.txt ve lengths.txt

String.txt:

abcdefghijklmnopqrstuvwxyz

lengths.txt

5
4
10
7

Dosyayı almak istiyorum

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

Yaklaşık 28.000 girişle çalışıyorum ve 200 ile 56.000 karakter arasında değişiyorlar.

Şu anda kullanıyorum:

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

Ama çok verimsiz. Daha iyi bir fikrin var mı?


Peki str="$(cat string.txt)"; i=0; while read j; do echo "${file:$i:$j}"; i=$((i+j)); done <length.txt.. sadece kabuk tarafından yapılan kadar hızlı görünüyor ..
heemayl

Dürüst olmak çok daha hızlı değil. Hala çok uzun zaman alıyor. Linux / programlama konusunda oldukça yeniyim, bu yüzden sadece kabuk kullanmanın daha hızlı bir yöntemi olmadığını düşünüyorsanız, fikirlere açığım.
user3891532

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

@jimmij, bir cevaba yapıştırmaya ne dersiniz
iruvar

Yanıtlar:


7

Yapabilirsin

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

Bazı açıklamalar gerektirir:

Ana fikir kullanmaktır { head ; } <fileve hafife alınan mikeserv cevabından elde edilir . Ancak bu durumda birçok heads kullanmamız gerekiyor , bu nedenle whiledöngü tanıtıldı ve headher iki dosyadan da girişe geçmek için dosya tanımlayıcılarıyla biraz ince ayar yapın ( String.txtişlemek için bir ana dosya olarak dosya ve length.txtbir argüman olarak -cseçenek olarak satırlar ) . Buradaki fikir, hız avantajının, String.txther komutun çağrıldığı headveya cutçağrıldığı her seferinde aramaya gerek duyulmaması gerektiğidir. echoHer tekrardan sonra yeni satır yazdırmak için adildir.

Ne kadar hızlı (varsa) ve >Entry_isatırlar arasına ekleme alıştırma olarak bırakılır.


G / Ç yönlendirmesinin düzgün kullanımı. Etiket Linux olduğundan, kabuğun Bash olduğunu varsayabilir ve read -u 3tanımlayıcı 3'ten okumak için kullanabilirsiniz .
Jonathan Leffler

@JonathanLeffler, Linux ile pek ilgisi yok bash. Linux tabanlı sistemlerin büyük çoğunluğu bashkurulu değildir (Android ve diğer gömülü sistemleri düşünün). bashBash geçiş hepsinden en yavaş kabuk olmaktan büyük olasılıkla degrade performans daha belirgin daha küçük kazanç olduğunu geçiş read <&3için read -u3(her durumda bir dış komutu gibi çalışan maliyetiyle karşılaştırıldığında önemsiz olacaktır hangi getirmek kudretini head). Yerleşik ksh93'e head(ve standart olmayan -cseçeneği destekleyen bire) geçmek performansları çok daha artıracaktır.
Stéphane Chazelas

head -c( headBu standart dışı seçeneğin kullanılabilir olduğu uygulamalar için) argümanının karakter değil, bir bayt olduğunu unutmayın. Bu çok baytlı yerel ayarlarda fark yaratacaktır.
Stéphane Chazelas

7

Genellikle, metni işlemek için kabuk döngülerini kullanmak istemezsiniz . Burada kullanacağım perl:

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

Bu bir komuttur ( readher seferinde bir bayt (veya normal dosyalar için birkaç bayt) okuyan kabuğun komutundan çok daha verimli bir şekilde arabelleğe yazarak ) her iki dosyayı yalnızca bir kez (bellekte tam olarak saklamadan) okur. kabuk komutlarında harici komutlar çalıştıran çözümlerden birkaç büyüklük sırası daha verimli olacaktır.

( -Cbu sayıların bayt sayısından farklı olarak geçerli yerel ayardaki karakter sayısı olması gerekiyorsa ekleyin. Örneğinizdeki gibi ASCII karakterleri için herhangi bir fark yaratmaz).


Bu $_, hem çıktı hem de girdi parametresi olarak kıvrımlı bir yeniden kullanımdır read, ancak koddaki bayt sayısını azaltır.
Jonathan Leffler

Hızlı bir testte (OP'nin örneği 100000 kez tekrarlandı), bu çözeltinin @ jimmij'lerin 1200 katı kadar hızlı olduğunu düşünüyorum (0.3 saniye vs 6 dakika (ile bash16 saniye PATH=/opt/ast/bin:$PATH ksh93)).
Stéphane Chazelas

6

bash, sürüm 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

çıktı

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

4

Ne olmuş awk?

process.awkBu kodla adlandırılan bir dosya oluşturun :

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

Kaydet ve yürüt awk -f process.awk lengths.txt string.txt


Kullanımına bağlı olarak PROCINFO, bu standart değil awk, ama gawk. Bu durumda başka bir gawktek özellik tercih ederdim FIELDWIDTHS:awk -vFIELDWIDTHS="$(tr '\n' ' ' < lengths.txt)" '{for(i=1;i<=NF;i++)print">Entry"i ORS$i}' string.txt
manatwork
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.