Çok satırlı kayıtları bölmeden büyük bir metin dosyasını verimli bir şekilde nasıl bölebilirim?


9

Ben büyük bir metin dosyası var (~ 50Gb gz'ed). Dosya 4*Nsatır veya Nkayıt içeriyor ; yani her kayıt 4 satırdan oluşur. Bu dosyayı giriş dosyasının yaklaşık% 25'i boyutunda 4 küçük dosyaya bölmek istiyorum. Dosyayı kayıt sınırında nasıl bölebilirim?

Saf bir yaklaşım zcat file | wc -lsatır sayısını elde etmek, bu sayıyı 4'e bölmek ve sonra kullanmak olacaktır split -l <number> file. Ancak, bu dosya üzerinde iki kez geçer ve satır sayısı son derece yavaştır (36 dakika). Daha iyi bir yol var mı?

Bu yaklaşıyor ama aradığım şey bu değil. Kabul edilen cevap bir satır sayımı da yapar.

DÜZENLE:

Dosya, fastq formatında sıralama verileri içerir. İki kayıt şöyle görünür (anonimleştirilmiş):

@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxTTTATGTTTTTAATTAATTCTGTTTCCTCAGATTGATGATGAAGTTxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFFFFFFFFFAFFFFF#FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF<AFFFFFFFFFFAFFFFFFFFFFFFFFFFFFF<FFFFFFFFFAFFFAFFAFFAFFFFFFFFAFFFFFFAAFFF<FAFAFFFFA
@NxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxGCGA+ATAGAGAG
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxCCCTCTGCTGGAACTGACACGCAGACATTCAGCGGCTCCGCCGCCxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
AAAAA#FFFFF7FFFFFFAFFFFA#F7FFFFFFFFF7FFFFFAF<FFFFFFFFFFFFFFAFFF.F.FFFFF.FAFFF.FFFFFFFFFFFFFF.)F.FFA))FFF7)F7F<.FFFF.FFF7FF<.FFA<7FA.<.7FF.FFFAFF

Her kaydın ilk satırı a ile başlar @.

EDIT2:

zcat file > /dev/null 31 dakika sürer.

EDIT3: Yalnızca ilk satırla başlar @. Diğerlerinin hiçbiri olmayacak. Buraya bakın . Kayıtların düzenli kalması gerekir. Ortaya çıkan dosyaya bir şey eklemek uygun değil.


Tek bir kişi ne kadar zcat file > /dev/nullsürer?
choroba

Söz konusu dosyanın küçük bir örneğini sağlayabilir misiniz?
FloHimself

Her kaydın başlayacağını @ve ayrıca kayıt başına 4 satır olduğunu söylüyorsunuz . Her ikisi de mutlak mı? - ve 2,3,4 satırları ile başlayabilir @mi? ve dosyada kayıt dışı altbilgi satırları var mı?
Peter.O

1
Sıkıştırılmış girdiyi işleyen ve / veya sıkıştırılmış çıktı üreten bir çözüm mü arıyorsunuz? Eşit boyutlu dört sıkıştırılmış dosya mı arıyorsunuz?
Stephen Kitt

Yanıtlar:


4

Bunu yapabileceğini sanmıyorum - güvenilir değil ve istediğin gibi değil. Mesele şu ki, arşivin sıkıştırma oranı muhtemelen kafadan kuyruğa eşit olarak dağıtılmayacak - sıkıştırma algoritması bazı parçalara diğerlerinden daha iyi uygulanacaktır. İşte böyle çalışır. Ve böylece bölünmüşlüğünüzü sıkıştırılmış dosyanın boyutuna dahil edemezsiniz.

Dahası gzip, sıkıştırılmış dosyaların orijinal boyutunun 4 gb'den daha büyük boyutlarda depolanmasını desteklemez - işleyemez. Böylece güvenilir bir boyut elde etmek için arşivi sorgulayamazsınız - çünkü sizi kandırır.

4 satır şey - bu gerçekten çok kolay. 4 dosya şey - sadece sıkıştırılmamış boyutunu almak için arşiv çıkarmadan önce güvenilir ve eşit bir dağıtım ile nasıl yapabileceğini bilmiyorum. Yapabileceğini sanmıyorum çünkü denedim.

Ancak, ne olabilir yapmak, split çıktı dosyaları için maksimum boyutu ayarlamak ve emin olun o zaman rekor bariyerlerindeki kırılır edilir. Kolayca yapabilirsiniz. Burada, gziparşivin çıkarılması ve içeriğin her bir dosyayı sıkıştırmak / yeniden sıkıştırmak için iletmeden önce ddbelirli count=$rptargümanlarla birkaç açık pipet tamponu aracılığıyla boruları oluşturarak yapacak küçük bir komut dosyası var lz4. Ayrıca teeher segmentin son dört satırını stderr'a basmak için birkaç küçük hile attım .

(       IFS= n= c=$(((m=(k=1024)*k)/354))
        b=bs=354xk bs=bs=64k
        pigz -d </tmp/gz | dd i$bs o$b |
        while   read -r line _$((n+=1))
        do      printf \\n/tmp/lz4.$n\\n
        { {     printf %s\\n "$line"
                dd count=$c i$b o$bs
        }|      tee /dev/fd/3|lz4 -BD -9 >/tmp/lz4.$n
        } 3>&1| tail -n4 |tee /dev/fd/2 |
                wc -c;ls -lh /tmp/[gl]z*
        done
)

Bu, tüm girdileri işleyene kadar devam edecektir. Onu, elde edemediği bir yüzdeye ayırmaya çalışmaz, bunun yerine bölünme başına maksimum ham bayt sayısına böler. Her neyse, probleminizin büyük bir kısmı, arşivinizde güvenilir bir boyut elde edemeyeceğinizdir, çünkü çok büyük - ne yaparsanız yapın, tekrar yapma - bu 4 gb'tan daha az bölünmeleri bu parçaya dönüştürün , olabilir. En azından bu küçük komut dosyası, diske sıkıştırılmamış bir bayt yazmak zorunda kalmadan bunu yapmanızı sağlar.

Aşağıda, temel bilgilerden çıkarılan daha kısa bir sürüm var - tüm rapor öğelerine eklenmiyor:

(       IFS= n= c=$((1024*1024/354))
        pigz -d | dd ibs=64k obs=354xk |
        while   read -r line _$((n+=1))
        do {    printf %s\\n "$line"
                dd count=$c obs=64k ibs=354xk
        }  |    lz4 -BD -9  >/tmp/lz4.$n
        done
)  </tmp/gz

Birincisi ile aynı şeyleri yapar, çoğunlukla bunun hakkında söyleyecek çok şeyi yoktur. Ayrıca, daha az karmaşa var, bu yüzden neler olduğunu görmek daha kolay olabilir.

IFS=Şey sadece bir idare etmektir readyinelemesi başına hattı. Biz readbiriz, çünkü girdi bittiğinde döngümüzün bitmesi gerekir. Bu , örnekleminize göre 354 bayt olan kayıt boyutunuza bağlıdır . gzipTest etmek için bazı rastgele verilerle 4 + gb arşiv oluşturdum .

Rasgele veriler şu şekilde elde edildi:

(       mkfifo /tmp/q; q="$(echo '[1+dPd126!<c]sc33lcx'|dc)"
        (tr '\0-\33\177-\377' "$q$q"|fold -b144 >/tmp/q)&
        tr '\0-\377' '[A*60][C*60][G*60][N*16][T*]' | fold -b144 |
        sed 'h;s/^\(.\{50\}\)\(.\{8\}\)/@N\1+\2\n/;P;s/.*/+/;H;x'|
        paste "-d\n" - - - /tmp/q| dd bs=4k count=kx2k  | gzip
)       </dev/urandom >/tmp/gz 2>/dev/null

... ama belki de zaten veriler ve her şeye sahip olduğunuz için bu konuda çok endişelenmenize gerek yok. Çözüme dönelim ...

Temel olarak pigz- ki bu, sıkıştırılmış olandan biraz daha hızlı zcataçılır - sıkıştırılmamış akışı dışarı ddatar ve özellikle 354 baytın katlarında boyutlandırılmış yazma bloklarına çıkan arabellekler. Döngü olacak readbir $lineo, girdi hala gelen olduğunu test etmek her tekrarında bir defa printfdaha sonra printfaz lz4bir önceki ddblok okuma olarak adlandırılır 354-bayt bir katına, özellikle boyutlu - tamponlama ile senkronize etmek için ddsürecin - süresince. İlkinden dolayı yineleme başına bir kısa okuma olacak read $line- ama bu önemli değil, çünkü yine de lz4- toplayıcı sürecimizde - yazdırıyoruz .

Her yinelemede yaklaşık 1 g sıkıştırılmamış veri okuyacak ve yayın içi 650Mb'ye kadar sıkıştıracak şekilde ayarladım. lz4diğer kullanışlı sıkıştırma yöntemlerinden çok daha hızlı - bu yüzden burada seçtim, çünkü beklemek istemiyorum. xzolsa da, gerçek sıkıştırmada çok daha iyi bir iş çıkarırdı. Bir şey hakkında lz4bir çok kez anlamı bir sıkıştırmasını - olsa da, genellikle serbest kalır RAM hızları için kapanışında yapabilirsiniz olduğunu lz4zaten belleğe yazma mümkün olacaktır tıpkı hızlı arşivi.

Büyük olan, yineleme başına birkaç rapor yapar. Her iki döngü de ddaktarılan ham bayt sayısı ve hızı vb. Büyük döngü ayrıca, döngü başına son 4 satır girişini ve bunun için bir bayt sayısını ve ardından arşivleri lsyazdığım dizinin bir tanesini yazdıracaktır lz4. İşte birkaç çıkış turu:

/tmp/lz4.1
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.838 s, 6.3 MB/s
@NTACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGC+TCTCTNCC
TACGTANTTCATTGGNATGACGCGCGTTTATGNGAGGGCGTCCGGAANGCTCTCTNCCGAGCTCAGTATGTTNNAAGTCCTGANGNGTNGCGCCTACCCGACCACAACCTCTACTCGGTTCCGCATGCATGCAACACATCGTCA
+
I`AgZgW*,`Gw=KKOU:W5dE1m=-"9W@[AG8;<P7P6,qxE!7P4##,Q@c7<nLmK_u+IL4Kz.Rl*+w^A5xHK?m_JBBhqaLK_,o;p,;QeEjb|">Spg`MO6M'wod?z9m.yLgj4kvR~+0:.X#(Bf
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1

/tmp/lz4.2
2961+1 records in
16383+1 records out
1073713090 bytes (1.1 GB) copied, 169.38 s, 6.3 MB/s
@NTTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGAC+CTTTTGCT
TTGTTGCCCTAACCANTCCTTGGGAACGCAATGGTGTGANCTGCCGGGACCTTTTGCTGCCCTGGTACTTTTGTCTGACTGGGGGTGCCACTTGCAGNAGTAAAAGCNAGCTGGTTCAACNAATAAGGACNANTTNCACTGAAC
+
>G-{N~Q5Z5QwV??I^~?rT+S0$7Pw2y9MV^BBTBK%HK87(fz)HU/0^%JGk<<1--7+r3e%X6{c#w@aA6Q^DrdVI0^8+m92vc>RKgnUnMDcU:j!x6u^g<Go?p(HKG@$4"T8BWZ<z.Xi
354

-rw-r--r-- 1 mikeserv mikeserv 4.7G Jun 16 08:58 /tmp/gz
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:32 /tmp/lz4.1
-rw-r--r-- 1 mikeserv mikeserv 652M Jun 16 12:35 /tmp/lz4.2

gzip -lyalnızca <2GiB sıkıştırılmamış dosyalar IIRC (OP'nin dosyasından daha küçük bir şey) için çalışır.
Stéphane Chazelas

@ StéphaneChazelas - kahretsin. Sıkıştırılmamış bir boyut elde etmenin tek yolu bu. Bu olmadan, bu hiç işe yaramıyor.
mikeserv

4

Kayıt sınırlarındaki dosyaları bölmek, herhangi bir kod olmadan aslında çok kolaydır:

zcat your_file.gz | split -l 10000 - output_name_

Bu, her biri 10000 satırdan oluşan çıktı dosyaları oluşturur, output_name_aa, output_name_ab, output_name_ac, ... Sizinki kadar büyük bir girdi ile, bu size çok sayıda çıktı dosyası verecektir. Dördün 10000herhangi bir katıyla değiştirin ve çıktı dosyalarını istediğiniz kadar büyük veya küçük yapabilirsiniz. Ne yazık ki, diğer cevaplarda olduğu gibi, giriş hakkında bazı tahminlerde bulunmadan istenen sayıda (yaklaşık) eşit boyutta çıktı dosyası alacağınızı garanti etmenin iyi bir yolu yoktur. (Ya da aslında her şeyi birbirine bağlar wc.) Kayıtlarınız yaklaşık olarak eşit büyüklükteyse (veya en azından kabaca eşit olarak dağıtıldıysa) aşağıdaki gibi bir tahmin bulmayı deneyebilirsiniz:

zcat your_file.gz | head -n4000 | gzip | wc -c

Bu, dosyanızın ilk 1000 kaydının sıkıştırılmış boyutunu gösterecektir. Buna dayanarak, muhtemelen her dosyada kaç satırın dört dosyayla sonuçlanmasını istediğinizi tahmin edebilirsiniz. (Dejenere beşinci bir dosyanın kalmasını istemiyorsanız, tahmininizi biraz doldurduğunuzdan emin olun veya beşinci dosyayı dördüncü kuyruğun kuyruğuna yapıştırmaya hazır olun.)

Düzenleme: Sıkıştırılmış çıktı dosyaları istediğinizi varsayarsak, bir numara daha:

#!/bin/sh

base=$(basename $1 .gz)
unpigz -c $1 | split -l 100000 --filter='pigz -c > _$FILE.gz' - ${base}_

batch=$((`ls _*.gz | wc -l` / 4 + 1))
for i in `seq 1 4`; do
  files=`ls _*.gz | head -$batch`
  cat $files > ${base}_$i.gz && rm $files
done

Bu, çok sayıda küçük dosya oluşturur ve daha sonra bunları hızlı bir şekilde yeniden birleştirir. (Dosyalarınızdaki satırların uzunluğuna bağlı olarak -l parametresini değiştirmeniz gerekebilir.) GNU coreutils'in (split --filter için) nispeten yeni bir sürümüne ve giriş dosya boyutunuzun yaklaşık% 130'una sahip olduğunuzu varsayar. Boş disk alanı. Eğer yoksa, gzip / zcat yerine pigz / unpigz kullanın. Bazı yazılım kitaplıklarının (Java?) Bu şekilde birleştirilmiş gzip dosyalarını işleyemediğini duydum, ancak şu ana kadar herhangi bir sorun yaşamadım. (pigz sıkıştırmayı paralelleştirmek için aynı hile kullanır.)


Yüklü pigz varsa, 'zcat' yerine 'pigz -cd'yi değiştirerek işleri biraz hızlandırabilirsiniz.
Drew

2
Ah, şimdi soruda zaten bölünmeden bahsettiğinizi fark ettim. Ama gerçekten, hemen hemen her çözüm, kaputun altındaki bölünme ile aynı şeyi yapacak. Zor kısım, her dosyaya kaç satır koymanız gerektiğini bulmaktır.
Drew

3

Ne google-küre kontrol ve ayrıca bir 7.8 GiB test ettikten sonra toplamak itibaren .gzdosyasını, orijinal sıkıştırılmamış dosyanın boyutunun meta (yani. Doğru olmadığını görünüyor yanlış geniş için) .gz4GiB (bazıları için belki 2GiB daha dosyaları (büyüktür sürümleri gzip.)
Re gzip meta benim testi.:

* The compressed.gz file is  7.8 GiB ( 8353115038 bytes) 
* The uncompressed  file is 18.1 GiB (19436487168 bytes)
* The metadata says file is  2.1 GiB ( 2256623616 bytes) uncompressed

Bu yüzden sıkıştırılmamış boyutu gerçekten sıkıştırmadan caydırmak mümkün görünmemektedir (en azından söylemek gerekirse biraz kaba!)

Her neyse, sıkıştırılmamış bir dosyayı, her kaydın 4 satır içerdiği kayıt sınırlarında bölmenin bir yolu .

Dosyanın boyutunu bayt cinsinden (üzerinden stat) ve awksayım baytlarıyla (karakterlerle değil) kullanır. Satır sonunun olup olmadığı LF| CR| CRLF, bu komut dosyası satır sonunu yerleşik değişken aracılığıyla işler RT).

LC_ALL=C gawk 'BEGIN{"stat -c %s "ARGV[1] | getline inSize
                      segSiz=int(inSize/4)+((inSize%4)==0?0:1)
                      ouSplit=segSiz; segNb=0 }
               { lnb++; bytCt+=(length+length(RT))
                 print $0 > ARGV[1]"."segNb
                 if( lnb!=4 ) next
                 lnb=0
                 if( bytCt>=ouSplit ){ segNb++; ouSplit+=segSiz }
               }' myfile

Aşağıda, her bir dosyanın satır sayısının mod 4 == 0

for i in myfile  myfile.{0..3}; do
    lc=$(<"$i" wc -l)
    printf '%s\t%s\t' "$i" $lc; 
    (( $(echo $lc"%4" | bc) )) && echo "Error: mod 4 remainder !" || echo 'mod 4 ok'  
done | column -ts$'\t' ;echo

Test çıktısı:

myfile    1827904  mod 4 ok
myfile.0  456976   mod 4 ok
myfile.1  456976   mod 4 ok
myfile.2  456976   mod 4 ok
myfile.3  456976   mod 4 ok

myfile tarafından oluşturuldu:

printf %s\\n {A..Z}{A..Z}{A..Z}{A..Z}—{1..4} > myfile

2

Bu ciddi bir cevap demek değildir! Ben sadece oynuyor flexve bu büyük olasılıkla ~ 50Gb (hiç değilse, test dosyamdan daha büyük giriş verilerinde) ile bir giriş dosyasında çalışmaz:

Bu benim için ~ 1Gb dosya input.txt üzerinde çalışır :

Splitter.lflex giriş dosyası verildiğinde :

%{
#include <stdio.h>
extern FILE* yyin;
extern FILE* yyout;

int input_size = 0;

int part_num;
int part_num_max;
char **part_names;
%}

%%
@.+ {
        if (ftell(yyout) >= input_size / part_num_max) {
            fclose(yyout);
            if ((yyout = fopen(part_names[++part_num], "w")) == 0) {
                exit(1);
            }
        }
        fprintf(yyout, "%s", yytext);
    }
%%

int main(int argc, char *argv[]) {

    if (argc < 2) {
        return 1;
    } else if ((yyin = fopen(argv[1], "r")) == 0) {
        return 1;
    } else if ((yyout = fopen(argv[2], "w")) == 0) {
        fclose(yyin);
        return 1;
    } else {

        fseek(yyin, 0L, SEEK_END);
        input_size = ftell(yyin);
        rewind(yyin);

        part_num = 0;
        part_num_max = argc - 2;
        part_names = argv + 2;

        yylex();

        fclose(yyin);
        fclose(yyout);
        return 0;
    }
}

üreten lex.yy.c ve bunu derleme splitterile ikili:

$ flex splitter.l && gcc lex.yy.c -ll -o splitter

Kullanımı:

$ ./splitter input.txt output.part1 output.part2 output.part3 output.part4

1Gb input.txt için çalışma süresi :

$ time ./splitter input.txt output.part1 output.part2 output.part3 output.part4

real    2m43.640s
user    0m48.100s
sys     0m1.084s

Buradaki gerçek lexing çok basit, lex'dan gerçekten faydalanmıyorsunuz. Sadece getc(stream)basit mantığı arayın ve uygulayın. Ayrıca, biliyor musunuz. (f) lex'daki (nokta) normal ifade karakteri, yeni satır dışındaki herhangi bir karakterle eşleşiyor , değil mi? Oysa bu kayıtlar çok satırlıdır.
Kaz

@Kaz İfadeleriniz genellikle çürük olsa da, bu aslında Q'da sağlanan verilerle çalışır.
FloHimself

Sadece yanlışlıkla, çünkü hiçbir şey eşleşmediğinde varsayılan bir kural vardır: bir karakteri tüket ve çıktıya yazdır! Diğer parolalarda, dosya değiştirme işlemini yalnızca @karakteri tanıyan bir kuralla yapabilir ve ardından varsayılan kuralın verileri kopyalamasına izin verebilirsiniz . Artık kuralınızın bir kısmını büyük bir jeton olarak kopyalamanıza ve ardından ikinci satırı bir kerede bir karakter almanıza sahipsiniz.
Kaz

Açıkladığınız için teşekkürler. Acaba, bu görevi nasıl çözersin txr.
FloHimself

Yapacağımdan emin değilim, çünkü görev mümkün olduğunca hızlı bir şekilde büyük miktarda veriyle çok basit bir şey yapmak.
Kaz

1

Python'da çıktı dosyalarını yazarken girdi dosyasının üzerinden bir geçiş yapan bir çözüm.

Kullanmayla ilgili bir özellik wc -l, buradaki kayıtların her birinin aynı boyutta olduğunu varsaymanızdır. Burada bu doğru olabilir, ancak aşağıdaki çözüm durum böyle olmasa bile işe yarar. Temelde wc -cveya dosyadaki bayt sayısını kullanıyor . Python'da bu, os.stat () aracılığıyla yapılır

Program şu şekilde işliyor. İlk olarak ideal ayrık noktaları bayt ofsetleri olarak hesaplıyoruz. Daha sonra, girdi dosyasının satırlarını uygun çıktı dosyasına yazarsınız. Eğer optimum sonraki split noktası aştınız gördüğünüzde ve size yakın son çıkış dosyasının, rekor sınırında ve sonraki açın.

Program bu anlamda en uygunudur, girdi dosyasının baytlarını bir kez okur; Dosya boyutunu elde etmek için dosya verilerinin okunması gerekmez. Gerekli depolama alanı bir çizginin boyutuyla orantılıdır. Ancak Python veya sistem muhtemelen G / Ç'yi hızlandırmak için makul dosya tamponlarına sahiptir.

İleride bunu ayarlamak istediğinizde kaç dosya bölündüğüne ve kayıt boyutunun ne olduğuna ilişkin parametreler ekledim.

Ve bu açıkça diğer programlama dillerine de çevrilebilir.

Başka bir şey, crlf ile Windows'un Unix-y sistemlerinde olduğu gibi çizginin uzunluğunu düzgün işleyip işlemediğinden emin değilim. Eğer len () burada biri tarafından kapalıysa, umarım programı nasıl ayarlayacağınız açıktır.

#!/usr/bin/env python
import os

# Adjust these
filename = 'file.txt'
rec_size = 4
file_splits = 4

size = os.stat(filename).st_size
splits = [(i+1)*size/file_splits for i in range(file_splits)]
with open(filename, 'r') as fd:
    linecount = 0
    i = 0 # File split number
    out = open('file%d.txt' % i, 'w')
    offset = 0  # byte offset of where we are in the file: 0..size
    r = 0 # where we are in the record: 0..rec_size-1
    for line in fd:
        linecount += 1
        r = (r+1) % rec_size
        if offset + len(line) > splits[i] and r == 1 :
            out.close()
            i += 1
            out = open('file%d.txt' % i, 'w')
        out.write(line)
        offset += len(line)
    out.close()
    print("file %s has %d lines" % (filename, linecount))

Rekor bir sınırda bölünmüyor. Örneğin. İlk alt dosya bölümü bu satırın 3. satırından sonra olurprintf %s\\n {A..Z}{A..Z}{A..Z}{A..Z}—{1..4}
Peter.O

1

Kullanıcı FloHimself bir TXR çözümü merak ediyor gibiydi . Gömülü TXR Lisp kullanan biri :

(defvar splits 4)
(defvar name "data")

(let* ((fi (open-file name "r"))                 ;; input stream
       (rc (tuples 4 (get-lines fi)))            ;; lazy list of 4-tuples
       (sz (/ (prop (stat name) :size) splits))  ;; split size
       (i 1)                                     ;; split enumerator
       (n 0)                                     ;; tuplecounter within split
       (no `@name.@i`)                           ;; output split file name
       (fo (open-file no "w")))                  ;; output stream
  (whilet ((r (pop rc)))  ;; pop each 4-tuple
    (put-lines r fo) ;; send 4-tuple into output file
    ;; if not on the last split, every 1000 tuples, check the output file
    ;; size with stat and switch to next split if necessary.
    (when (and (< i splits)
               (> (inc n) 1000)
               (>= (seek-stream fo 0 :from-current) sz))
      (close-stream fo)
      (set fo (open-file (set no `@name.@(inc i)`) "w")
           n 0)))
  (close-stream fo))

Notlar:

  1. Aynı nedenden popötürü - tembel tuples listesinden her tupleın alınması önemlidir, böylece tembel listenin tüketilmesi gerekir. Bu listenin başlangıcına bir atıfta bulunmamalıyız, çünkü dosya içinde ilerledikçe bellek büyüyecektir.

  2. (seek-stream fo 0 :from-current)op-no'lu bir durumdur seek-stream, bu da mevcut konumu döndürerek kendisini faydalı kılar.

  3. Performans: bundan bahsetme. Kullanılabilir, ancak herhangi bir kupayı eve getirmeyecek.

  4. Her 1000 tuple sadece boyut kontrolü yaptığımızdan, tuple boyutu 4000 satır yapabiliriz.


0

Orijinal dosyanın bitişik parçaları olması için yeni dosyalara ihtiyacınız yoksa, bunu tamamen sedşu şekilde yapabilirsiniz:

sed -n -e '1~16,+3w1.txt' -e '5~16,+3w2.txt' -e '9~16,+3w3.txt' -e '13~16,+3w4.txt'

-nHer satırı baskı durdurur ve her -ekomut aslında aynı şeyi yapıyor. 1~16ilk satırla ve sonraki her 16 satırla eşleşir. ,+3her birinden sonraki üç satırı eşleştirmek anlamına gelir. w1.txtdiyor ki, tüm bu satırları dosyaya yaz 1.txt. Bu, 4 satırlık her 4 grubu alıp 4 satırlık ilk gruptan başlayarak bir dosyaya yazıyor. Diğer üç komut aynı şeyi yapar, ancak her biri 4 satır ileriye kaydırılır ve farklı bir dosyaya yazılır.

Dosya, ortaya koyduğunuz spesifikasyonla tam olarak eşleşmezse bu korkunç bir şekilde kırılacaktır, ancak aksi takdirde istediğiniz gibi çalışmalıdır. Ben profilli değil, bu yüzden ne kadar verimli olacağını bilmiyorum, ama sedakış düzenleme makul derecede etkilidir.

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.