Zamień ciąg w dużym (70 GB), jednym wierszu pliku tekstowym

126

Mam ogromny (70 GB), jeden wiersz , plik tekstowy i chcę w nim zastąpić ciąg (token). Chcę zastąpić token <unk>innym tokenem zastępczym ( problem z rękawiczkami ).

Próbowałem sed:

sed 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new

ale plik wyjściowy corpus.txt.newma zero bajtów!

Próbowałem także użyć perla:

perl -pe 's/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new

ale wystąpił błąd braku pamięci.

W przypadku mniejszych plików działają oba powyższe polecenia.

Jak mogę zamienić ciąg znaków na taki plik? To powiązane pytanie, ale żadna z odpowiedzi nie zadziałała dla mnie.

Edycja : Co powiesz na podzielenie pliku na części po 10 GB (lub cokolwiek innego) i zastosowanie seddo każdego z nich, a następnie scalenie ich cat? Czy to ma sens? Czy istnieje bardziej eleganckie rozwiązanie?

Christos Baziotis
źródło
jak zauważył @Gilles, czy możesz wykryć jakiś powtarzający się znak, który mógłby służyć jako niestandardowy separator w pojedynczej dużej linii?
RomanPerekhrest
Myślę, że narzędzie, które może tylko wyszukiwać i zamieniać, ale nie bardziej złożone wyrażenia regularne, byłoby szybsze. Nie przyniosłoby to również korzyści z robienia linii na raz, więc nie udusiłoby się tego pliku. Niestety nie mam pojęcia o istnieniu takiego narzędzia, choć nie byłoby trudno napisać. Jeśli jest to jednorazowe, zastąpienie znakami nowej linii, tak jak w jednej z odpowiedzi, prawdopodobnie byłoby najłatwiejsze.
ctrl-alt-delor
Czy Twój plik zawiera coś innego niż ASCII? Jeśli tak, cała obsługa Unicode może zostać pominięta, a nieprzetworzone bajty mogą być przetwarzane.
Patrick Bucher
Zgadzam się z @PatrickButcher Spójrz na większe zdjęcie. Oprócz pilnej potrzeby zastąpienia tego tekstu, do czego jeszcze ten plik ma być używany? Jeśli jest to jakiś dziennik, nikt nie będzie w stanie efektywnie z nim pracować. Jeśli jest to plik danych używany przez niektóre aplikacje, wówczas ta aplikacja powinna ponosić odpowiedzialność za utrzymanie danych w tym pliku.
Thomas Carlisle,
2
Możesz używać splitz -bopcją definiowania wielkości plików porcji w bajtach. Przetwarzaj każdy po kolei za pomocą sedi ponownie złóż. Istnieje ryzyko, że <unk>można je podzielić na dwa pliki i nie można ich znaleźć ...
Vladislavs Dovgalecs,

Odpowiedzi:

106

Zwykłe narzędzia do przetwarzania tekstu nie są zaprojektowane do obsługi linii, które nie mieszczą się w pamięci RAM. Mają tendencję do pracy, czytając jeden rekord (jedną linię), manipulując nim i wyprowadzając wynik, a następnie przechodząc do następnego rekordu (linii).

Jeśli w pliku często pojawia się znak ASCII i nie pojawia się on w <unk>lub <raw_unk>, możesz użyć go jako separatora rekordów. Ponieważ większość narzędzi nie pozwala na niestandardowe separatory rekordów, zamień między tym znakiem a znakami nowej linii. trprzetwarza bajty, a nie wiersze, więc nie ma znaczenia wielkość rekordu. Załóżmy, że to ;działa:

<corpus.txt tr '\n;' ';\n' |
sed 's/<unk>/<raw_unk>/g' |
tr '\n;' ';\n' >corpus.txt.new

Możesz także zakotwiczyć pierwszy znak szukanego tekstu, zakładając, że nie jest on powtarzany w wyszukiwanym tekście i pojawia się wystarczająco często. Jeśli plik może zaczynać się unk>, zmień polecenie sed, sed '2,$ s/…aby uniknąć fałszywego dopasowania.

<corpus.txt tr '\n<' '<\n' |
sed 's/^unk>/raw_unk>/g' |
tr '\n<' '<\n' >corpus.txt.new

Możesz też użyć ostatniego znaku.

<corpus.txt tr '\n>' '>\n' |
sed 's/<unk$/<raw_unk/g' |
tr '\n>' '>\n' >corpus.txt.new

Zauważ, że ta technika zakłada, że ​​sed działa bezproblemowo na pliku, który nie kończy się na nowej linii, tzn. Przetwarza ostatnią część linii bez obcinania jej i bez dołączania ostatniej linii. Działa z GNU sed. Jeśli możesz wybrać ostatni znak pliku jako separator rekordów, unikniesz problemów z przenośnością.

Gilles
źródło
8
Nie mam takiego pliku do przetestowania, ale w Awk możesz określić „Record Separator” i „Output Record Separator”. Zakładając, że w twoim pliku znajduje się sporo przecinków, możliwe, że możesz to rozwiązać za pomocą: awk -v RS=, -v ORS=, '{gsub(/<unk>/, "<raw_unk>"); print}' Nie?
Wildcard
4
@Wildcard Tak, to inne rozwiązanie. Awk jest jednak wolniejszy niż sed, dlatego nie oferuję go jako preferowanego rozwiązania dla dużego pliku.
Gilles
Możesz ustawić separator rekordów w Perlu z opcją wiersza poleceń -0i ósemkową wartością char, lub w skrypcie można ustawić specjalną zmienną$/
beasy
@Gilles: Ale używając awkunikaj dwukrotnego przekazywania strumienia tr. Czy byłoby jeszcze wolniej?
user285259
2
@ user285259 Zwykle nie. trjest bardzo szybki, a rura może być nawet równoległa.
Gilles
110

W przypadku tak dużego pliku jedną z możliwości jest Flex. Niech unk.lbędzie:

%%
\<unk\>     printf("<raw_unk>");  
%%

Następnie skompiluj i uruchom:

$ flex -o unk.c  unk.l
$ cc -o unk -O2 unk.c -lfl
$ unk < corpus.txt > corpus.txt.new
JJoao
źródło
5
makema do tego domyślne reguły, zamiast flex / cc możesz dodać %option mainjako pierwszą linię unk.l, a potem tylko make unk. Mniej lub bardziej odruchowo używam %option main 8bit fasti mam export CFLAGS='-march=native -pipe -Os'w sobie .bashrc.
jthill
1
@undercat: Gdyby to nie było poza tematem, mógłbym pokazać ci wiele aplikacji niekompilatorowych, od rozwiązania problemu z poziomem wody do analizy danych wejściowych specjalnego przeznaczenia. To niesamowite, co możesz z tym zrobić, jeśli myślisz trochę
nieszablonowo
@jthill, dziękuję: %option main+ make+ opcjonalnie CFLAGSsą bardzo fajną sztuczką !! Czy -march=nativezachowanie jest domyślne?
JJoao
1
@jamesqf, jak powiedziałeś - trudno będzie zadać to pytanie na temat - ale chciałbym to również zobaczyć
Steven Penny
1
@jamesqf Jeden z moich profesorów na uni użył Flex do zbudowania narzędzia, które rozpoznaje rodzaje tkanin dla fabryki! A co powiesz na coś takiego: „flex wydaje się bardzo potężnym narzędziem, ale raczej nie piszę żadnych kompilatorów / parserów - czy są jakieś inne przypadki użycia flexa?”
Paul Evans
41

Nie masz więc wystarczającej ilości pamięci fizycznej (RAM), aby pomieścić cały plik na raz, ale w systemie 64-bitowym masz wystarczająco dużo wirtualnej przestrzeni adresowej, aby zmapować cały plik. Wirtualne mapowania mogą być przydatne jako prosty hack w takich przypadkach.

Niezbędne operacje są zawarte w Pythonie. Istnieje kilka irytujących subtelności, ale unika się pisania kodu C. W szczególności należy zachować ostrożność, aby uniknąć kopiowania pliku do pamięci, co całkowicie zniweczyłoby ten problem. Z drugiej strony otrzymujesz bezpłatne raportowanie błędów (python „wyjątki”) :).

#!/usr/bin/python3
# This script takes input from stdin
# (but it must be a regular file, to support mapping it),
# and writes the result to stdout.

search = b'<unk>'
replace = b'<raw_unk>'


import sys
import os
import mmap

# sys.stdout requires str, but we want to write bytes
out_bytes = sys.stdout.buffer

mem = mmap.mmap(sys.stdin.fileno(), 0, access=mmap.ACCESS_READ)
i = mem.find(search)
if i < 0:
    sys.exit("Search string not found")

# mmap object subscripts to bytes (making a copy)
# memoryview object subscripts to a memoryview object
# (it implements the buffer protocol).
view = memoryview(mem)

out_bytes.write(view[:i])
out_bytes.write(replace)
out_bytes.write(view[i+len(search):])
sourcejedi
źródło
Jeśli mój system ma około 4 GB wolnej pamięci z 8 GB, to czy mem = mmap.mmap (sys.stdin.fileno (), 0, access = mmap.ACCESS_READ) oznacza, że ​​umieszcza dane w tym miejscu? A może byłby znacznie niższy (1 gb?)>
Rahul
1
@Rahul „Więc nie masz wystarczającej ilości pamięci RAM, ale w 64-bitowym systemie masz wystarczająco dużo wirtualnej przestrzeni adresowej, aby zmapować cały plik.” Jest wczytywany i wychodzący z fizycznego ramana na żądanie (lub jego brak). Ten program powinien działać bez wymaganej dużej ilości fizycznej pamięci RAM. Systemy 64-bitowe mają znacznie więcej wirtualnej przestrzeni adresowej niż maksymalny fizyczny RAM. Również każdy uruchomiony proces ma własną wirtualną przestrzeń adresową. Oznacza to, że w całym systemie zabrakło wirtualnej przestrzeni adresowej nie jest rzeczą, nie jest prawidłową koncepcją.
sourcejedi
4
@Rahul tak! python mmap.mmap () jest dość cienkim opakowaniem wokół funkcji C mmap (). A mmap () to ten sam mechanizm, który służy do uruchamiania plików wykonywalnych i kodu z bibliotek współdzielonych.
sourcejedi
2
@jamesqf Mogę się mylić, ale czuję, że to tylko osobisty wybór. Ponieważ straty wydajności byłyby znikome (ponieważ, jak powiedział, funkcja faktycznie wywołuje funkcję c), marnotrawstwo kosztów ogólnych jest bardzo niskie, ponieważ pomiędzy nimi nie występują żadne inne rzeczy. C byłby lepszy, ale to rozwiązanie nie miało na celu optymalizacji, tylko rozwiązanie większego i trudniejszego problemu 70 GB.
Rahul
1
Ogólnie rzecz biorąc, pisanie w Pythonie jest bardziej kompaktowe. W tym przypadku okazało się, że wersja Pythona zawiera kilka szczegółów, a wersja C mogłaby być ładniejsza do napisania. (Chociaż nie jest to takie proste, jeśli searchmoże zawierać znak NUL. I zauważam, że inna wersja C tutaj nie obsługuje znaków NUL replace.). Zapraszamy do uzyskania wersji C w celach porównawczych. Pamiętaj jednak, że moja wersja zawiera podstawowe raportowanie błędów dla wykonywanych operacji. Wersja C będzie przynajmniej bardziej irytujące czytać IMO, gdy raportowanie błędów jest włączone.
sourcejedi
17

Myślę, że wersja C może działać znacznie lepiej:

#include <stdio.h>
#include <string.h>

#define PAT_LEN 5

int main()
{
    /* note this is not a general solution. In particular the pattern
     * must not have a repeated sequence at the start, so <unk> is fine
     * but aardvark is not, because it starts with "a" repeated, and ababc
     * is not because it starts with "ab" repeated. */
    char pattern[] = "<unk>";          /* set PAT_LEN to length of this */
    char replacement[] = "<raw_unk>"; 
    int c;
    int i, j;

    for (i = 0; (c = getchar()) != EOF;) {
        if (c == pattern[i]) {
            i++;
            if (i == PAT_LEN) {
                printf("%s", replacement);
                i = 0;
            }
        } else {
            if (i > 0) {
                for (j = 0; j < i; j++) {
                    putchar(pattern[j]);
                }
                i = 0;
            }
            if (c == pattern[0]) {
                i = 1;
            } else {
                putchar(c);
            }
        }
    }
    /* TODO: fix up end of file if it ends with a part of pattern */
    return 0;
}

EDYCJA: Zmodyfikowana zgodnie z sugestiami z komentarzy. Naprawiono również błąd we wzorze <<unk>.

Patrick Bucher
źródło
2
możesz wydrukować (wzór [j]) zamiast (buf [j]) (w tym momencie są równe, więc nie potrzebujesz bufora
RiaD
3
również kod nie będzie działał dla łańcucha „<” ideone.com/ncM2yy
RiaD
10
30 MB w 0,3 sekundy? To tylko 90 MB / sekundę. memcpySzybkość (tj. wąskie gardło pamięci) to około 12 GB / sekundę na najnowszym procesorze x86 (np. Skylake). Nawet w przypadku narzutu wywołania systemowego stdio +, w przypadku pliku o pojemności 30 MB w buforze pamięci podręcznej dysku, oczekiwałbym może 1 GB / sekundę dla wydajnej implementacji. Czy skompilowałeś kompilację z wyłączoną optymalizacją, czy jest to naprawdę tak wolno we / wy? getchar_unlocked/ putchar_unlockedmoże pomóc, ale zdecydowanie lepiej jest czytać / pisać w kawałkach o wielkości może 128kiB (połowa wielkości pamięci podręcznej L2 na większości procesorów x86, więc najczęściej trafiasz w L2 podczas zapętlania po przeczytaniu)
Peter Cordes
2
z góry głowy getchar i putchar wolne.
Rui F Ribeiro,
3
fixDo programu "<<unk>"nadal nie działa, jeśli patternzaczyna się od powtarzających się sekwencji znaków (czyli nie będzie działać, jeśli starali się zastąpić Mrówkojad z zebry i trzeba było wejście aaardvak, albo starali się zastąpić ababc i miał wkład abababc). Ogólnie rzecz biorąc, nie możesz przejść do przodu o liczbę przeczytanych znaków, chyba że wiesz, że nie ma możliwości dopasowania się do przeczytanych znaków.
icarus
16

W replacepakiecie mariadb-server / mysql-server znajduje się narzędzie. Zastępuje proste łańcuchy (nie wyrażenia regularne) i w przeciwieństwie do grep / sed / awk replacenie dba o \ni \0. Zużycie pamięci jest stałe dla każdego pliku wejściowego (około 400 KB na moim komputerze).

Oczywiście nie musisz uruchamiać serwera mysql, aby go używać replace, jest on spakowany tylko w ten sposób w Fedorze. Inne dystrybucje / systemy operacyjne mogą mieć to oddzielnie.

legolegs
źródło
14

GNU grepmoże pokazać przesunięcie dopasowania w plikach „binarnych”, bez konieczności wczytywania całych linii do pamięci. Następnie możesz użyć dddo odczytu do tego przesunięcia, pominąć dopasowanie, a następnie kontynuować kopiowanie z pliku.

file=...
newfile=...
replace='<raw_unk>'
grep -o -b -a -F '<unk>' <"$file" |
(   pos=0
    while IFS=$IFS: read offset pattern
    do size=${#pattern}
       let skip=offset-pos
       let big=skip/1048576
       let skip=skip-big*1048576
       dd bs=1048576 count=$big <&3
       dd bs=1 count=$skip <&3
       dd bs=1 count=$size of=/dev/null <&3
       printf "%s" "$replace"
       let pos=offset+size
    done
    cat <&3
) 3<"$file" >"$newfile"

Dla szybkości podzieliłem ddduży odczyt wielkości bloku 1048576 i mniejszy odczyt 1 bajtu naraz, ale ta operacja nadal będzie trochę powolna na tak dużym pliku. Dane grepwyjściowe są na przykład 13977:<unk>podzielone na dwukropek przez odczyt na zmienne offseti pattern. Musimy śledzić, posile bajtów zostało już skopiowanych z pliku.

meuh
źródło
11

Oto kolejna pojedyncza linia poleceń UNIX, która może działać lepiej niż inne opcje, ponieważ można „polować” na „rozmiar bloku”, który działa dobrze. Aby było to solidne, musisz wiedzieć, że masz co najmniej jedną spację na każde X znaków, gdzie X jest twoim dowolnym „rozmiarem bloku”. W poniższym przykładzie wybrałem „rozmiar bloku” wynoszący 1024 znaki.

fold -w 1024 -s corpus.txt | sed 's/<unk>/<raw_unk>/g' | tr '/n' '/0'

Tutaj fold spakuje do 1024 bajtów, ale -s upewnia się, że łamie się na spacji, jeśli jest co najmniej jeden od ostatniej przerwy.

Polecenie sed należy do ciebie i robi to, czego oczekujesz.

Następnie polecenie tr „rozłoży” plik konwertujący nowe wiersze, które zostały wstawione z powrotem do niczego.

Powinieneś rozważyć wypróbowanie większych bloków, aby sprawdzić, czy działa ono szybciej. Zamiast 1024 możesz wypróbować 10240 oraz 102400 i 1048576 dla opcji -w fold.

Oto przykład w podziale według każdego kroku, który konwertuje wszystkie litery N na małe litery:

[root@alpha ~]# cat mailtest.txt
test XJS C4JD QADN1 NSBN3 2IDNEN GTUBE STANDARD ANTI UBE-TEST EMAIL*C.34X test

[root@alpha ~]# fold -w 20 -s mailtest.txt
test XJS C4JD QADN1
NSBN3 2IDNEN GTUBE
STANDARD ANTI
UBE-TEST
EMAIL*C.34X test

[root@alpha ~]# fold -w 20 -s mailtest.txt | sed 's/N/n/g'
test XJS C4JD QADn1
nSBn3 2IDnEn GTUBE
STAnDARD AnTI
UBE-TEST
EMAIL*C.34X test

[root@alpha ~]# fold -w 20 -s mailtest.txt | sed 's/N/n/g' | tr '\n' '\0'
test XJS C4JD QADn1 nSBn3 2IDnEn GTUBE STAnDARD AnTI UBE-TEST EMAIL*C.34X test

Będziesz musiał dodać nowy wiersz na samym końcu pliku, jeśli go ma, ponieważ polecenie tr go usunie.

alfreema
źródło
1
Jak upewnić się, że nie łamiesz wzoru w przypadkach krawędzi, w których nie ma wystarczającej ilości białych znaków?
rackandboneman
1
Jak już wspomniano, aby było to solidne, wymagane jest, aby co najmniej jedno miejsce na X znaków było dostępne. Możesz przeprowadzić tę analizę dość łatwo, z dowolnym wybranym blokiem: fold -w X mailtest.txt | grep -v "" | wc -l Zwracana liczba to liczba linii zagiętych z potencjalnymi przypadkami krawędzi. Jeśli jest to zero, gwarantuje się, że rozwiązanie zadziała.
alfreema
10

Za pomocą perl

Zarządzanie własnymi buforami

Możesz użyć IO::Handle's setvbufdo zarządzania domyślnymi buforami lub możesz zarządzać swoimi własnymi buforami za pomocą sysreadi syswrite. Sprawdź perldoc -f sysreadi perldoc -f syswritepo więcej informacji, zasadniczo pomijają buforowane io.

Tutaj rzucamy naszym własnym buforem IO, ale robimy to ręcznie i arbitralnie na 1024 bajtach. Otwieramy również plik RW, więc robimy to wszystko na tym samym FH na raz.

use strict;
use warnings;
use Fcntl qw(:flock O_RDWR);
use autodie;
use bytes;

use constant CHUNK_SIZE => 1024 * 32;

sysopen my $fh, 'file', O_RDWR;
flock($fh, LOCK_EX);

my $chunk = 1;
while ( sysread $fh, my $bytes, CHUNK_SIZE * $chunk ) {
  if ( $bytes =~ s/<unk>/<raw_unk>/g ) {
    seek( $fh, ($chunk-1)* CHUNK_SIZE, 0 );
    syswrite( $fh, $bytes, 1024);
    seek( $fh, $chunk * CHUNK_SIZE, 0 );
  }
  $chunk++;
}

Jeśli zamierzasz wybrać tę trasę

  1. Upewnij się, <unk>i <raw_unk>są tej samej wielkości bajt.
  2. Możesz upewnić się, że nasza buforowana metoda nie przekracza CHUNKSIZEgranicy, jeśli zastępujesz więcej niż 1 bajt.
Evan Carroll
źródło
2
Co jeśli <unk>spadnie na granicę między kawałkami?
liori
8

Możesz spróbować bbe ( edytor bloków binarnych ), „ seddla plików binarnych”.

Odniosłem duży sukces, używając go w pliku tekstowym o pojemności 7 GB bez EOLznaków, zastępując wiele wystąpień ciągu jednym o różnej długości. Bez próby optymalizacji dało to średnią przepustowość przetwarzania> 50 MB / s.

owirt
źródło
5

Dzięki perlmożesz pracować z rekordami o stałej długości, takimi jak:

perl -pe 'BEGIN{$/=\1e8}
          s/<unk>/<raw_unk>/g' < corpus.txt > corpus.txt.new

I mam nadzieję, że nie będzie <unk>dwóch takich 100 MB rekordów.

Stéphane Chazelas
źródło
Myślałem również o tej metodzie, ale używając while read -N 1000 chunk;( 1000wybrano jako przykład). Rozwiązaniem problemu z <unk>podziałem na fragmenty są dwa przejścia przez plik: pierwszy z fragmentami 100 MB, a drugi z fragmentami „100 MB + 5 bajtów”. Ale nie jest to optymalne rozwiązanie w przypadku pliku 70 GB.
MiniMax,
3
Nie potrzebujesz nawet dwóch przepustek. Czytaj blok A. Nie będąc EOF, czytaj blok B. Wyszukaj / zamień w A + B. Odp .: = B. Pętla. Złożoność zapewnia, że ​​nie wymienisz wewnątrz zamiennika.
roaima
@MiniMax, ten drugi przebieg niekoniecznie pomógłby, ponieważ pierwszy przebieg dodałby 5 bajtów dla każdego wystąpienia <unk>.
Stéphane Chazelas,
1
@roaima, tak, byłoby to znacznie bardziej zaangażowane rozwiązanie. Tutaj jest to proste podejście, które jest wysoce prawdopodobne (zakładając, że <unk>zdarzenia są dalekie, jeśli nie, użyj $/ = ">"i s/<unk>\z/<raw_unk>/g) poprawności.
Stéphane Chazelas,
5

Oto mały program Go, który wykonuje zadanie ( unk.go):

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

func main() {
    const (
        pattern     = "<unk>"
        replacement = "<raw_unk>"
    )
    var match int
    var char rune
    scanner := bufio.NewScanner(os.Stdin)
    scanner.Split(bufio.ScanRunes)
    for scanner.Scan() {
        char = rune(scanner.Text()[0])
        if char == []rune(pattern)[match] {
            match++
            if match == len(pattern) {
                fmt.Print(replacement)
                match = 0
            }
        } else {
            if match > 0 {
                fmt.Print(string(pattern[:match]))
                match = 0
            }
            if char == rune(pattern[0]) {
                match = 1
            } else {
                fmt.Print(string(char))
            }
        }
    }
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }
}

Po prostu zbuduj go go build unk.goi uruchom jako ./unk <input >output.

EDYTOWAĆ:

Przepraszam, nie przeczytałem, że wszystko jest w jednej linii, więc próbowałem teraz odczytać plik znak po znaku.

EDYCJA II:

Zastosowano taką samą poprawkę jak dla programu C.

Patrick Bucher
źródło
1
czy pozwala to uniknąć odczytu całego pliku do pamięci?
kot
1
Odczytuje plik znak po znaku i nigdy nie przechowuje całego pliku w pamięci, tylko pojedyncze znaki.
Patrick Bucher
1
scanner.Split(bufio.ScanRunes)robi magię.
Patrick Bucher
Sprawdź także go doc bufio.MaxScanTokenSizedomyślny rozmiar bufora.
Patrick Bucher
Podobnie jak twój Cprogram, nie działa to w przypadku zamiany aardwarku na zebrę z wejściem aaardwarku.
icarus
1

Może to być nadmiar w przypadku pliku o pojemności 70 GB oraz proste wyszukiwanie i zamiana, ale platforma Hadoop MapReduce rozwiązałaby teraz twój problem bez żadnych kosztów (wybierz opcję „Single Node” podczas konfigurowania, aby uruchomić go lokalnie) - i może być skalowane do nieskończonej pojemności w przyszłości bez potrzeby modyfikowania kodu.

Oficjalny samouczek na https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html używa (bardzo prostej) Java, ale można znaleźć biblioteki klienta dla Perla lub w jakim języku chcesz.

Więc jeśli później okaże się, że wykonujesz bardziej złożone operacje na plikach tekstowych o wielkości 7000 GB - i musisz to robić 100 razy dziennie - możesz rozłożyć obciążenie na wiele węzłów, które udostępniasz lub które są automatycznie udostępniane dla Ciebie przez chmurę - oparty klaster Hadoop.

Sam Rahimi
źródło
1
tak tak to jest. „Nie używaj Hadoop - twoje dane nie są takie duże” . Jest to bardzo prosty problem z IO przesyłania strumieniowego.
sourcejedi
0

Wszystkie poprzednie sugestie wymagają odczytania całego pliku i zapisania całego pliku. Zajmuje to nie tylko dużo czasu, ale także wymaga 70 GB wolnego miejsca.

1) Jeśli dobrze rozumiem konkretny przypadek, czy dopuszczalne byłoby zastąpienie innym ciągiem o takiej samej długości?

2a) Czy występuje wiele wystąpień? 2b) Jeśli tak, to ile wiesz?

Jestem pewien, że rozwiązałeś już ten ponad rok problem i chciałbym wiedzieć, jakiego rozwiązania użyłeś.

Zaproponowałbym rozwiązanie (najprawdopodobniej w C), które czytałoby BLOKI pliku przeszukując każdy ciąg, biorąc pod uwagę możliwe krzyżowanie bloków. Po znalezieniu zamień ciąg na przemienny o tej samej długości i zapisz tylko ten BLOK. Kontynuacja dla znanej liczby wystąpień lub do końca pliku. Wymagałoby to zaledwie kilku zapisów i co najwyżej dwukrotnie więcej (jeśli każde wystąpienie zostało podzielone na 2 bloki). Nie wymagałoby to dodatkowej przestrzeni!

DGerman
źródło
-1

Jeśli mamy minimalną kwotę <unk>(zgodnie z prawem Zipf),

awk -v RS="<unk>" -v ORS="<raw_unk>" 1
JJoao
źródło
1
Nie sed. Niezależnie od tego czyta wiersz do pamięci. Nie będzie w stanie zmieścić się w tej linii.
Kusalananda
1
Nie mogę znaleźć żadnej dokumentacji, która mówi coś innego niż to, że GNU sednie będzie buforować wejścia / wyjścia podczas używania tej flagi. Nie widzę, że będzie czytać wiersze częściowe.
Kusalananda