2016-01-25 37 views
6

Mam pierwszeństwo poleceń powłoki, aby wszystko załatwić. Mam bardzo, bardzo duży plik - około 2,8 GB, a zawartość jest podobna do JSON. Wszystko jest w jednym wierszu i powiedziano mi, że jest tam co najmniej 1,5 miliona rekordów.Wyszukaj i zamień ciąg znaków w bardzo dużym pliku.

Muszę przygotować plik do spożycia. Każdy rekord musi znajdować się w osobnej linii. Próbka:

{"RomanCharacters":{"Alphabet":[{"RecordId":"1",...]},{"RecordId":"2",...},{"RecordId":"3",...},{"RecordId":"4",...},{"RecordId":"5",...} }} 

też użyć następujących ...

{"Accounts":{"Customer":[{"AccountHolderId":"9c585258-c94c-442b-a2f0-1ebbcc274795","Title":"Mrs","Forename":"Tina","Surname":"Wright","DateofBirth":"1988-01-01","Contact":[{"Contact_Info":"9168777943","TypeId":"Mobile Number","PrimaryFlag":"No","Index":"1","Superseded":"No" },{"Contact_Info":"9503588153","TypeId":"Home Telephone","PrimaryFlag":"Yes","Index":"2","Superseded":"Yes" },{"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"No","Index":"3","Superseded":"No" },{"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"Yes","Index":"4","Superseded":"Yes" }, {"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"No","Index":"5","Superseded":"NO" },{"Contact_Info":"15482475584","TypeId":"Mobile_Phone","PrimaryFlag":"No","Index":"6","Superseded":"No" }],"Address":[{"AddressPtr":"5","Line1":"Flat No.14","Line2":"Surya Estate","Line3":"Baner","Line4":"Pune ","Line5":"new","Addres_City":"pune","Country":"India","PostCode":"AB100KP","PrimaryFlag":"No","Superseded":"No"},{"AddressPtr":"6","Line1":"A-602","Line2":"Viva Vadegiri","Line3":"Virar","Line4":"new","Line5":"banglow","Addres_City":"Mumbai","Country":"India","PostCode":"AB10V6T","PrimaryFlag":"Yes","Superseded":"Yes"}],"Account":[{"Field_A":"6884133655531279","Field_B":"887.07","Field_C":"A Loan Product",...,"FieldY_":"2015-09-18","Field_Z":"24275627"}]},{"AccountHolderId":"92a5788f-cd8f-423d-ae5f-4eb0ceb457fd","_Title":"Dr","_Forename":"Christopher","_Surname":"Carroll","_DateofBirth":"1977-02-02","Contact":[{"Contact_Info":"9168777943","TypeId":"Mobile Number","PrimaryFlag":"No","Index":"7","Superseded":"No" },{"Contact_Info":"9503588153","TypeId":"Home Telephone","PrimaryFlag":"Yes","Index":"8","Superseded":"Yes" },{"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"No","Index":"9","Superseded":"No" },{"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"Yes","Index":"10","Superseded":"Yes" }],"Address":[{"AddressPtr":"11","Line1":"Flat No.14","Line2":"Surya Estate","Line3":"Baner","Line4":"Pune ","Line5":"new","Addres_City":"pune","Country":"India","PostCode":"AB11TXF","PrimaryFlag":"No","Superseded":"No"},{"AddressPtr":"12","Line1":"A-602","Line2":"Viva Vadegiri","Line3":"Virar","Line4":"new","Line5":"banglow","Addres_City":"Mumbai","Country":"India","PostCode":"AB11O8W","PrimaryFlag":"Yes","Superseded":"Yes"}],"Account":[{"Field_A":"4121879819185553","Field_B":"887.07","Field_C":"A Loan Product",...,"Field_X":"2015-09-18","Field_Z":"25679434"}]},{"AccountHolderId":"4aa10284-d9aa-4dc0-9652-70f01d22b19e","_Title":"Dr","_Forename":"Cheryl","_Surname":"Ortiz","_DateofBirth":"1977-03-03","Contact":[{"Contact_Info":"9168777943","TypeId":"Mobile Number","PrimaryFlag":"No","Index":"13","Superseded":"No" },{"Contact_Info":"9503588153","TypeId":"Home Telephone","PrimaryFlag":"Yes","Index":"14","Superseded":"Yes" },{"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"No","Index":"15","Superseded":"No" },{"Contact_Info":"[email protected]","TypeId":"Email Address","PrimaryFlag":"Yes","Index":"16","Superseded":"Yes" }],"Address":[{"AddressPtr":"17","Line1":"Flat No.14","Line2":"Surya Estate","Line3":"Baner","Line4":"Pune ","Line5":"new","Addres_City":"pune","Country":"India","PostCode":"AB12SQR","PrimaryFlag":"No","Superseded":"No"},{"AddressPtr":"18","Line1":"A-602","Line2":"Viva Vadegiri","Line3":"Virar","Line4":"new","Line5":"banglow","Addres_City":"Mumbai","Country":"India","PostCode":"AB12BAQ","PrimaryFlag":"Yes","Superseded":"Yes"}],"Account":[{"Field_A":"3288214945919484","Field_B":"887.07","Field_C":"A Loan Product",...,"Field_Y":"2015-09-18","Field_Z":"66264768"}]}]}} 

końcowy wynik powinien być:

{"RomanCharacters":{"Alphabet":[{"RecordId":"1",...]}, 
{"RecordId":"2",...}, 
{"RecordId":"3",...}, 
{"RecordId":"4",...}, 
{"RecordId":"5",...} }} 

Próby komendy:

  • sed -e 's/,{"RecordId"/}]},\n{"RecordId"/g' sample.dat
  • awk '{gsub(",{\"RecordId\"",",\n{\"RecordId\"",$0); print $0}' sample.dat

Podjęta komenda działa idealnie dobrze dla małych plików. Ale nie działa to dla pliku 2.8 GB, który muszę manipulować. Sed wychodzi w połowie po 10 minutach bez powodu i nic nie zostało zrobione. Awk został uszkodzony z powodu awarii segmentacji (core dump) po wielu godzinach. Próbowałem wyszukiwać i zamieniać Perla i otrzymałem komunikat o błędzie "Brak pamięci".

Każda pomoc/pomysły będą świetne!

Dodatkowe info na moim komputerze: przestrzeń dyskowa

  • Ponad 105 GB dostępny.
  • 8 GB pamięci
  • 4 rdzeni procesora
  • ubuntu 14.04
+0

Potrzebujemy lepszych przykładowych danych - nie musi to być zrzut Twoich danych, ale ilustracja problemu. Również - czy rozważałeś użycie parsera? – Sobrique

+1

Podstawowy problem, jak sądzę, polega na tym, że wszystkie trzy z tych narzędzi czytają liniowo, a tym samym powstają w wyniku "pojedynczej ogromnej linii". Spróbuj najpierw przetworzyć najpierw coś takiego jak "tr", "\ 012", aby zastąpić przecinki nowymi wierszami. Wtedy narzędzia liniowe będą działały lepiej. –

+0

Spróbuj ponownie za pomocą perla, ale ustaw $/na ",". Spróbuj także parametru "-u" dla sed (--buffffered). – neuhaus

Odpowiedz

3

chodzi Perl: Spróbuj ustawić separator linii wejściowej $/ do }, tak:

#!/usr/bin/perl 
$/= "},"; 
while (<>){ 
    print "$_\n"; 
}' 

lub jako jedną wkładką:

$ perl -e '$/="},";while(<>){print "$_\n"}' sample.dat 
+1

Udowodniono, że działa to sprawnie. Zmodyfikowałem twój skrypt, aby znaleźć rozwiązanie tego problemu. Wkrótce się nim podzielę. – dat789

+0

Może warto rzucić okiem na flagi '-n' i' -p'. Myślę, że możesz napisać to jako: 'perl -pe 'BEGIN {$/="}, "} print" \ n "; } ' – Sobrique

2

Użyj } jako separator zapisu, np w Perlu:

perl -l -0175 -ne 'print $_, $/' < input 

Być może trzeba przykleić z powrotem linie zawierające tylko }.

2

Pozwala to uniknąć problemu z pamięcią, nie patrząc na dane jako pojedynczy rekord, ale może pójść za daleko w drugą stronę, jeśli chodzi o wydajność (przetwarzanie pojedynczego znaku na raz). Należy również pamiętać, że wymaga gawk dla wbudowanego RT zmiennej (wartości bieżącej separator rekordu):

$ cat j.awk 
BEGIN { RS="[[:print:]]" } 
RT == "{" { bal++} 
RT == "}" { bal-- } 
{ printf "%s", RT } 
RT == "," && bal == 2 { print "" } 
END { print "" } 

$ gawk -f j.awk j.txt 
{"RomanCharacters":{"Alphabet":[{"RecordId":"1",...]}, 
{"RecordId":"2",...}, 
{"RecordId":"3",...}, 
{"RecordId":"4",...}, 
{"RecordId":"5",...} }} 
4

Od otagowaniu zapytanie z sed, awk i Perl, wnoszę, że to, czego naprawdę potrzebują jest zaleceniem dla narzędzia. Chociaż jest to trochę nie na temat, uważam, że jq jest coś, co można by do tego użyć. Będzie lepiej niż sed lub awk, ponieważ w rzeczywistości rozumie JSON. Wszystko, co pokazano tutaj z jq, można również zrobić w perlu z odrobiną programowania.

Zakładając treści jak poniżej (w oparciu o próby):

{"RomanCharacters":{"Alphabet": [ {"RecordId":"1","data":"data"},{"RecordId":"2","data":"data"},{"RecordId":"3","data":"data"},{"RecordId":"4","data":"data"},{"RecordId":"5","data":"data"} ] }} 

Można łatwo sformatować to "upiększać" go:

$ jq '.' < data.json 
{ 
    "RomanCharacters": { 
    "Alphabet": [ 
     { 
     "RecordId": "1", 
     "data": "data" 
     }, 
     { 
     "RecordId": "2", 
     "data": "data" 
     }, 
     { 
     "RecordId": "3", 
     "data": "data" 
     }, 
     { 
     "RecordId": "4", 
     "data": "data" 
     }, 
     { 
     "RecordId": "5", 
     "data": "data" 
     } 
    ] 
    } 
} 

I możemy kopać w danych do pobieraj tylko te rekordy, które Cię interesują (niezależnie od tego, co są pakowane):

$ jq '.[][][]' < data.json 
{ 
    "RecordId": "1", 
    "data": "data" 
} 
{ 
    "RecordId": "2", 
    "data": "data" 
} 
{ 
    "RecordId": "3", 
    "data": "data" 
} 
{ 
    "RecordId": "4", 
    "data": "data" 
} 
{ 
    "RecordId": "5", 
    "data": "data" 
} 

jest znacznie bardziej czytelny, zarówno przez ludzi, jak i przez narzędzia, takie jak awk, który przetwarza zawartość line-by-line. Jeśli chcesz dołączyć swoje linie do przetwarzania na swoim pytaniu awk staje się znacznie bardziej prosta:

$ jq '.[][][]' < data.json | awk '{printf("%s ",$0)} /}/{printf("\n")}' 
{ "RecordId": "1", "data": "data" } 
{ "RecordId": "2", "data": "data" } 
{ "RecordId": "3", "data": "data" } 
{ "RecordId": "4", "data": "data" } 
{ "RecordId": "5", "data": "data" } 

Albo, jak @peak sugerowane w komentarzach, wyeliminować część awk od standardów NY całkowicie za pomocą -c (wyjście kompaktowy JQ za) opcja:

$ jq -c '.[][][]' < data.json 
{"RecordId":"1","data":"data"} 
{"RecordId":"2","data":"data"} 
{"RecordId":"3","data":"data"} 
{"RecordId":"4","data":"data"} 
{"RecordId":"5","data":"data"} 
+0

Należy zauważyć, że opcja -q -c może być tutaj wykorzystana. W przykładzie przy użyciu data.json, 'jq -c '. [] [] []' peak

+0

@peak - genialny, dziękuję ci za to, z jakiegoś powodu, który nigdy mi nie przyszedł. :-) Dodałem to do mojej odpowiedzi. – ghoti

0

Korzystanie z przykładowych danych dostarczonych tutaj (ten, który zaczyna się od {Kont: {klienta ...), rozwiązaniem tego problemu jest taki, który czyta w plik i jak to czyta, zlicza liczbę ograniczników zdefiniowanych w $ /. Dla każdej liczby 10 000 ograniczników wypisze nowy plik. I dla każdego znalezionego separatora nadaje mu nową linię. Oto jak skrypt wygląda następująco:

#!/usr/bin/perl 

$base="/home/dat789/incoming"; 
#$_="sample.dat"; 

$/= "}]},"; # delimiter to find and insert new line after 
$n = 0; 
$match=""; 
$filecount=0; 
$recsPerFile=10000; # set number of records in a file 

print "Processing " . $_ ."\n"; 

while (<>){ 
    if ($n < $recsPerFile) { 
     $match=$match.$_."\n"; 
     $n++; 
     print "."; #This is so that we'd know it has done something 
    }  
    else { 
     my $newfile="partfile".$recsPerFile."-".$filecount . ".dat"; 
     open (OUTPUT,'>', $newfile); 
     print OUTPUT $match; 
     $match=""; 
     $filecount++; 
     $n=0; 
    print "Wrote file " . $newfile . "\n"; 
    } 
} 

print "Finished\n\n"; 

Użyłem tego skryptu na dużym 2,8 GB pliku, w którym jest to treść jest sformatowana JSON jedno-liner. W wynikowych plikach brakuje prawidłowych nagłówków i stopek JSON, ale można to łatwo naprawić.

Dziękuję, chłopaki za wniesienie wkładu!