Remplacer la chaîne dans un fichier texte énorme (70 Go), une ligne


126

J'ai un énorme (70 Go), une ligne , un fichier texte et je veux remplacer une chaîne (jeton) en elle. Je souhaite remplacer le jeton <unk>par un autre jeton factice ( problème de gant ).

J'ai essayé sed:

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

mais le fichier de sortie corpus.txt.newa zéro octet!

J'ai aussi essayé d'utiliser Perl:

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

mais j'ai eu une erreur de mémoire insuffisante.

Pour les fichiers plus petits, les deux commandes ci-dessus fonctionnent.

Comment puis-je remplacer une chaîne est un tel fichier? C'est une question liée, mais aucune des réponses n'a fonctionné pour moi.

Edit : Qu'en est-il de scinder le fichier en morceaux de 10 Go (ou quoi que ce soit) chacun, d'appliquer sedchacun d'eux et de les fusionner ensuite cat? Cela a-t-il du sens? Y a-t-il une solution plus élégante?


comme @Gilles l’a noté, pouvez-vous détecter un caractère répété qui pourrait servir de délimiteur personnalisé dans votre seule grande ligne?
RomanPerekhrest

Je pense qu'un outil qui ne peut que rechercher et remplacer, mais pas les regex plus complexes, serait plus rapide. De plus, il ne gagnerait pas à faire une ligne à la fois et ne s'étoufferait donc pas dans ce dossier. Malheureusement, je n'ai aucune idée de l'existence d'un tel outil, même s'il ne serait pas difficile à écrire. S'il s'agit d'un élément isolé, il serait probablement plus simple de remplacer les caractères d'une nouvelle ligne par une des réponses.
ctrl-alt-delor

Votre fichier contient-il autre chose qu'ASCII? Dans ce cas, toute la gestion unicode pourrait être omise et les octets bruts pourraient être traités.
Patrick Bucher

Je suis d'accord avec @PatrickButcher Regardez une image plus grande. Outre le besoin immédiat de remplacer ce texte, à quoi d'autre ce fichier est-il censé être utilisé? Si c'est un journal, personne ne sera capable de le travailler efficacement. S'il s'agit d'un fichier de données utilisé par une application, celle-ci devrait être tenue responsable de la conservation des données de ce fichier.
Thomas Carlisle

2
Vous pouvez utiliser l’ option splitavec la -bdéfinition de la taille des fichiers de morceaux en octets. Traitez chacun à son tour en utilisant sedet le ré-assembler. Il y a un risque, c'est que l' <unk>on puisse diviser en deux fichiers sans les retrouver ...
Vladislavs Dovgalecs le

Réponses:


106

Les outils de traitement de texte habituels ne sont pas conçus pour gérer les lignes qui ne rentrent pas dans la RAM. Ils ont tendance à travailler en lisant un enregistrement (une ligne), en le manipulant et en affichant le résultat, puis en passant à l'enregistrement suivant (ligne).

Si un caractère ASCII apparaît fréquemment dans le fichier et n'apparaît pas dans <unk>ou <raw_unk>, vous pouvez l'utiliser comme séparateur d'enregistrement. Comme la plupart des outils n'autorisent pas les séparateurs d'enregistrement personnalisés, permutez entre ce caractère et les nouvelles lignes. trtraite des octets, pas des lignes, de sorte qu'il ne se soucie pas de la taille d'un enregistrement. En supposant que cela ;fonctionne:

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

Vous pouvez également ancrer le premier caractère du texte que vous recherchez, en supposant qu'il ne soit pas répété dans le texte recherché et qu'il apparaisse assez souvent. Si le fichier peut commencer par unk>, modifiez la commande sed sed '2,$ s/…pour éviter une correspondance parasite.

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

Sinon, utilisez le dernier caractère.

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

Notez que cette technique suppose que sed fonctionne de manière transparente sur un fichier qui ne se termine pas par une nouvelle ligne, c'est-à-dire qu'il traite la dernière ligne partielle sans la tronquer ni l'ajout d'une nouvelle ligne. Cela fonctionne avec GNU sed. Si vous pouvez choisir le dernier caractère du fichier comme séparateur d'enregistrement, vous éviterez tout problème de portabilité.


8
Je n'ai pas un tel fichier à tester, mais dans Awk, vous pouvez spécifier le "séparateur d'enregistrement" et le "séparateur d'enregistrement de sortie". Donc, en supposant que vous ayez une poignée de virgules dans votre fichier, il est possible de résoudre ce problème avec: awk -v RS=, -v ORS=, '{gsub(/<unk>/, "<raw_unk>"); print}' Non?
Wildcard

4
@Wildcard Oui, c'est une autre solution. Awk a tendance à être plus lent que sed, c’est pourquoi je ne l’offre pas comme solution privilégiée pour un fichier volumineux.
Gilles

Vous pouvez définir le séparateur d'enregistrement en Perl avec l'option de ligne de commande -0et la valeur octale d'un caractère, ou à l'intérieur du script, il peut être défini avec une variable spéciale$/
beasy

@ Gilles: Mais awkévitez de passer deux fois au flux tr. Alors serait-il encore plus lent?
user285259

2
@ user285259 Généralement pas. trest très rapide et le tuyau peut même être parallélisé.
Gilles

110

Pour un si gros fichier, une possibilité est Flex. Let unk.lÊtre:

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

Ensuite, compilez et exécutez:

$ flex -o unk.c  unk.l
$ cc -o unk -O2 unk.c -lfl
$ unk < corpus.txt > corpus.txt.new

5
makea des règles par défaut pour cela, à la place du flex / cc, vous pouvez ajouter un %option maincomme première ligne de unk.l puis juste make unk. J'utilise plus ou moins par réflexe %option main 8bit fastet j'ai export CFLAGS='-march=native -pipe -Os'dans mes .bashrc.
Jthill

1
@undercat: S'il n'était pas hors sujet, je pourrais vous montrer un certain nombre d'applications frontales sans compilateur, allant de la résolution du problème du niveau d'eau à l'analyse syntaxique d'entrées à des fins spécifiques. C'est incroyable ce que vous pouvez faire avec, si vous pensez un peu en dehors de la boîte :-)
jamesqf

@jthill, merci: %option main+ make+ optionnellement CFLAGSc'est un très beau tour !! Est-ce que -march=nativele comportement par défaut?
JJoao

1
@ jamesqf comme vous l'avez dit - il sera difficile de faire de cette question un sujet d'actualité - mais j'aimerais le voir aussi
Steven Penny

1
@ jamesqf Un de mes professeurs à l'université a utilisé flex pour créer un outil qui reconnaît les types de tissus d'une usine! Pourquoi ne pas demander quelque chose comme: "flex semble être un outil très puissant, mais il est peu probable que j'écrive des compilateurs / analyseurs syntaxiques - existe-t-il d'autres cas d'utilisation de flex?"
Paul Evans

40

Par conséquent, vous ne disposez pas de suffisamment de mémoire physique (RAM) pour contenir le fichier entier en une fois, mais sur un système 64 bits, vous disposez de suffisamment d' espace d'adressage virtuel pour mapper l'ensemble du fichier. Les mappages virtuels peuvent être utiles comme un simple piratage dans des cas comme celui-ci.

Les opérations nécessaires sont toutes incluses dans Python. Il y a plusieurs subtilités gênantes, mais cela évite d'avoir à écrire du code C. En particulier, il faut éviter de copier le fichier en mémoire, ce qui éliminerait complètement le problème. Sur le plan positif, vous obtenez un rapport d’erreurs gratuitement (python "exceptions") :).

#!/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):])

Si Mon système a environ 4 Go de mémoire supplémentaire libre sur 8 Go, est-ce que mem = mmap.mmap (sys.stdin.fileno (), 0, access = mmap.ACCESS_READ) signifie-t-il qu'il place les données dans cet espace? Ou serait - il (1Go?) Beaucoup plus bas>
Rahul

1
@Rahul "Donc, vous n'avez pas assez de RAM, mais sur un système 64 bits, vous avez assez d' espace d'adressage virtuel pour mapper l'intégralité du fichier." Il est paginé dans et hors du bélier physique à la demande (ou son absence). Ce programme devrait fonctionner sans nécessiter une grande quantité de RAM physique. Les systèmes 64 bits ont beaucoup plus d'espace d'adressage virtuel que le ram physique maximum. De plus, chaque processus en cours possède son propre espace d'adressage virtuel. Cela signifie que le système dans son ensemble manque d'espace d'adressage virtuel n'est pas une chose, ce n'est pas un concept valide.
sourcejedi

4
@Rahul yep! python mmap.mmap () est une enveloppe assez mince autour de la fonction C mmap (). Et mmap () est le même mécanisme utilisé pour exécuter les exécutables et le code des bibliothèques partagées.
sourcejedi

2
@ jamesqf Je peux me tromper, mais j'estime qu'il ne s'agit que d'un choix personnel. Étant donné que les pertes de performances seraient négligeables (car, comme il l'a dit, la fonction réelle appelle la fonction c), le gaspillage de frais généraux est très faible, puisqu’aucun autre incident ne se produit entre les deux. C aurait été mieux, mais cette solution ne visait pas l’optimisation, mais simplement le problème plus complexe et complexe des 70 Go.
Rahul

1
En général, l'écriture en python est plus compacte. Dans ce cas, il est apparu que la version python contenait quelques détails, et que la version C aurait peut-être été plus agréable à écrire. (Bien que ce ne soit pas si simple si searchpeut contenir un caractère NUL. Et je remarque que l’autre version C ici ne prend pas en charge les caractères NUL dans replace.). Vous pouvez très bien dériver la version C à des fins de comparaison. Cependant, rappelez-vous que ma version inclut un rapport d'erreur de base pour les opérations effectuées. La version C serait au moins plus gênante pour lire IMO, lorsque le rapport d’erreur est inclus.
sourcejedi le

16

Il existe un replaceutilitaire dans le paquet mariadb-server / mysql-server. Il remplace des chaînes simples (expressions régulières) pas et contrairement à grep / sed / awk replacene se soucie pas \net \0. La consommation de mémoire est constante quel que soit le fichier d'entrée (environ 400 Ko sur ma machine).

Bien sûr, vous n'avez pas besoin de faire tourner un serveur mysql pour l'utiliser replace, il est seulement emballé de cette façon dans Fedora. Il peut être emballé séparément dans d’autres systèmes d’exploitation / systèmes d’exploitation.


16

Je pense que la version C pourrait fonctionner beaucoup mieux:

#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;
}

EDIT: Modifié selon les suggestions des commentaires. Également corrigé un bug avec le motif <<unk>.


2
vous pouvez imprimer (modèle [j]) au lieu de (buf [j]) (ils sont égaux à ce stade, vous n'avez donc pas besoin de tampon
RiaD

3
le code ne fonctionnera pas non plus pour la chaîne "<< unk
RiaD

10
30 Mo en 0,3 seconde? C'est seulement 90 Mo / seconde. memcpyLa vitesse (c’est-à-dire le goulot d’étranglement de la mémoire) est d’environ 12 Go / seconde sur un processeur x86 récent (par exemple, Skylake). Même avec la surcharge d'appels système stdio +, pour un fichier de 30 Mo à chaud dans le cache disque, je m'attendrais peut-être à 1 Go / seconde pour une mise en œuvre efficace. Avez-vous compilé avec l'optimisation désactivée ou les E / S à un caractère à la fois sont-elles vraiment aussi lentes? getchar_unlocked/ putchar_unlockedpourrait aider, mais il est certainement préférable de lire / écrire en morceaux de peut-être 128kiB (la moitié de la taille du cache L2 sur la plupart des processeurs x86, donc vous frappez surtout en L2 en boucle après lecture)
Peter Cordes

2
du haut de ma tête, getchar et putchar sont lents.
Rui F Ribeiro

3
Le fixprogramme pour "<<unk>"toujours ne fonctionne pas si il patterncommence par une séquence répétée de caractères (cela ne fonctionnerait pas si vous tentiez de remplacer aardvark par zebra et si vous aviez entré aaardvak, ou si vous essayiez de remplacer ababc et eu entrée de abababc). En général, vous ne pouvez pas avancer du nombre de caractères que vous avez lus, à moins que vous sachiez qu’il n’ya aucune possibilité qu’une correspondance commence avec les caractères que vous avez lus.
icarus

14

GNU greppeut vous montrer le décalage des correspondances dans des fichiers "binaires", sans avoir à lire des lignes entières en mémoire. Vous pouvez ensuite utiliser ddpour lire jusqu’à ce décalage, ignorer la correspondance, puis continuer à copier à partir du fichier.

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"

Pour dddes raisons de rapidité, j'ai scindé le en une grande lecture de la taille de bloc 1048576 et une lecture plus petite d'un octet à la fois, mais cette opération sera encore un peu lente sur un fichier aussi volumineux. La grepsortie est, par exemple, 13977:<unk>et elle est divisée sur les deux points par la lecture en variables offsetet pattern. Nous devons garder trace posdu nombre d'octets déjà copiés du fichier.


11

Voici une autre ligne de commande UNIX unique qui peut fonctionner mieux que d’autres options, car vous pouvez "rechercher" une "taille de bloc" performante. Pour que cela soit robuste, vous devez savoir que vous avez au moins un espace dans chaque X caractères, où X est votre "taille de bloc" arbitraire. Dans l'exemple ci-dessous, j'ai choisi une "taille de bloc" de 1024 caractères.

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

Ici, fold se chargera jusqu'à 1024 octets, mais le -s s'assurera qu'il saute sur un espace s'il y en a au moins un depuis la dernière pause.

La commande sed est à vous et fait ce que vous attendez.

Ensuite, la commande tr "déplie" le fichier en convertissant les nouvelles lignes insérées.

Vous devriez envisager d’essayer des blocs plus grands pour voir s’il fonctionne plus rapidement. Au lieu de 1024, vous pouvez essayer 10240, 102400 et 1048576 pour l'option -w de fold.

Voici un exemple décomposé à chaque étape qui convertit tous les N en minuscules:

[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

Vous devrez ajouter une nouvelle ligne à la toute fin du fichier, le cas échéant, car la commande tr le supprimera.


1
Comment vous assurez-vous de ne pas briser le schéma dans les cas où il n’ya pas assez d’espaces libres?
rackandboneman

1
Comme indiqué, pour être robuste, il faut au moins un espace pour chaque X caractères. Vous pouvez effectuer cette analyse assez facilement, quelle que soit la taille de votre choix: fold -w X mailtest.txt | grep -v "" | wc -l Le nombre renvoyé est le nombre de lignes pliées avec des cas limites éventuels. Si c'est zéro, la solution est garantie au travail.
alfreema

10

En utilisant perl

Gérer vos propres tampons

Vous pouvez utiliser IO::Handle« s setvbufpour gérer les tampons par défaut, ou vous pouvez gérer vos propres tampons avec sysreadet syswrite. Vérifiez perldoc -f sysreadet perldoc -f syswritepour plus d’informations, essentiellement, ils sautent la mémoire tampon io.

Ici, nous lançons notre propre tampon IO, mais nous le faisons manuellement et arbitrairement sur 1024 octets. Nous ouvrons également le fichier pour RW, donc nous le faisons tous en même temps sur le même FH.

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++;
}

Si vous allez suivre cette voie

  1. Assurez-vous <unk>que <raw_unk>la taille en octets est identique.
  2. Vous voudrez peut-être vous assurer que notre méthode en mémoire tampon ne dépasse pas la CHUNKSIZElimite si vous remplacez plus d'un octet.

2
Et si <unk>tombe sur une limite entre des morceaux?
Liori

8

Vous pouvez essayer bbe ( éditeur de blocs binaires ), un " sedpour les fichiers binaires".

J'ai eu du succès à l'utiliser sur un fichier texte de 7 Go sans EOLcaractères, remplaçant plusieurs occurrences d'une chaîne par une autre de longueur différente. Sans aucune tentative d'optimisation, le débit de traitement moyen était supérieur à 50 Mo / s.


5

Avec perl, vous pouvez travailler avec des enregistrements de longueur fixe comme:

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

Et espérons qu’il n’y aura pas de <unk>débordement entre deux de ces enregistrements de 100 Mo.


Je pensais aussi à cette méthode, mais en utilisant le while read -N 1000 chunk;( 1000choisi comme exemple). La solution pour le <unk>, cassé entre les morceaux, consiste en deux passages dans le fichier: le premier avec les morceaux de 100 Mo et le second avec les morceaux '100 Mo + 5 octets'. Mais ce n'est pas la solution optimale dans le cas du fichier de 70 Go.
MiniMax

3
Vous n'avez même pas besoin de deux passes. Lire le bloc A. Lisez le bloc B. Bien que non EOF, recherchez / remplacez en A + B. A: = B. Boucle. La complexité est de s'assurer que vous ne remplacez pas le remplaçant.
roaima

@MiniMax, ce second passage n’aidera pas forcément car le premier passage aurait ajouté 5 octets pour chaque occurrence de <unk>.
Stéphane Chazelas

1
@roaima, oui, ce serait une solution beaucoup plus complexe. Ici, il s’agit d’une approche simple et hautement probable (en supposant que les <unk>occurrences soient très éloignées, sinon, utilisez $/ = ">"et s/<unk>\z/<raw_unk>/g) exacte.
Stéphane Chazelas

5

Voici un petit programme Go qui effectue la tâche ( 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)
    }
}

Il suffit de le construire avec go build unk.goet l’exécuter en tant que ./unk <input >output.

MODIFIER:

Désolé, je n'ai pas lu que tout est dans une ligne, alors j'ai essayé de lire le fichier caractère par caractère maintenant.

EDIT II:

Appliqué même correctif que pour le programme C.


1
cela évite-t-il de lire le fichier entier en mémoire?
Chat

1
Il lit le fichier caractère par caractère et ne conserve jamais la totalité du fichier dans la mémoire, mais uniquement des caractères individuels.
Patrick Bucher

1
scanner.Split(bufio.ScanRunes)fait la magie.
Patrick Bucher

Vérifiez également go doc bufio.MaxScanTokenSizela taille de la mémoire tampon par défaut.
Patrick Bucher

Comme votre Cprogramme, cela ne fonctionne pas pour remplacer aardvark par zebra par une entrée de aaardvark.
icarus

1

Cela peut être excessif pour un fichier de 70 Go et une simple recherche et remplacement, mais le framework Hadoop MapReduce résoudrait votre problème sans aucun frais (choisissez l’option 'Noeud unique' lors de la configuration pour l’exécuter localement) - et peut être mise à l'échelle à une capacité infinie à l'avenir sans avoir à modifier votre code.

Le tutoriel officiel à l' adresse https://hadoop.apache.org/docs/stable/hadoop-mapreduce-client/hadoop-mapreduce-client-core/MapReduceTutorial.html utilise Java (extrêmement simple) mais vous pouvez trouver des bibliothèques clientes pour Perl ou quelle que soit la langue que vous avez envie d'utiliser.

Par conséquent, si, par la suite, vous réalisez des opérations plus complexes sur des fichiers texte de 7 000 Go, et que vous devez le faire 100 fois par jour, vous pouvez répartir la charge de travail sur plusieurs nœuds que vous avez provisionnés ou qui sont automatiquement provisionnés par un cloud. cluster Hadoop basé.


1
Oui oui ça l'est. "N'utilisez pas Hadoop - vos données ne sont pas si grandes" . C'est un problème très simple de streaming IO.
sourcejedi

0

Toutes les suggestions précédentes nécessitent de lire le fichier entier et de l'écrire au complet. Cela prend non seulement beaucoup de temps, mais nécessite également 70 Go d'espace libre.

1) serait-il correctement Si je vous comprends cas spécifique acceptable pour remplacer <UNK> avec une autre chaîne de la même longueur?

2a) Y a-t-il plusieurs occurrences? 2b) Si oui, savez-vous combien?

Je suis sûr que vous avez déjà résolu ce problème de l'année en cours et j'aimerais savoir quelle solution vous avez utilisée.

Je proposerais une solution (probablement en C) qui lirait les BLOCS du fichier en cherchant chacun la chaîne en tenant compte du croisement possible des blocs. Une fois la substitution trouvée, remplacez la chaîne par la longueur SAME en alternance et écrivez uniquement ce BLOCK. Continuant pour le nombre connu d'occurrences ou jusqu'à la fin du fichier. Cela nécessiterait aussi peu que le nombre d'occurrences écrites et au plus le double (si chaque occurrence était divisée en 2 blocs). Cela ne nécessiterait AUCUN espace supplémentaire!


-1

Si nous avons un montant minimum de <unk>(comme prévu par la loi de Zipf),

awk -v RS="<unk>" -v ORS="<raw_unk>" 1

1
Non, sedlit une ligne à la fois dans la mémoire, peu importe. Il ne pourra pas s'adapter à cette ligne.
Kusalananda

1
Je ne trouve aucune documentation indiquant que GNU sedn'effectuera pas la mise en mémoire tampon des entrées / sorties lors de l'utilisation de cet indicateur. Je ne vois pas qu'il lira des lignes partielles.
Kusalananda
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.