6 votes

Lecture d'un fichier volumineux et sortie des sections correspondant à plusieurs paramètres

Je n'ai que rarement affaire à des scripts, et je me heurte donc à un manque de connaissances sur ce problème.

J'ai un fichier >500mb en texte, qui est bien sectionné, mais je sais qu'il y a 5 à 10 "mauvaises" sections à l'intérieur. Les données contenues dans les sections peuvent être évaluées assez facilement par un humain, mais je ne sais pas comment le faire dans un programme.

J'achète une bonne valeur connue en #Field MyField - Toutefois, si cette valeur n'apparaît pas dans le #FIELD LOCATION mais quelque chose n'a pas fonctionné.

Voici un exemple de deux sections dans le fichier. La première est "mauvaise" et la seconde est "bonne".

#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
  1. Les sections commencent et se terminent logiquement, avec #START y #END

  2. Si #FIELD LOCATION n'existe pas, passer à la section suivante

  3. Si #FIELD MyField="BAR" y #FIELD LOCATION ne contient pas BAR , imprime toutes les lignes de cette section dans un nouveau fichier.

  4. Note - Clarification de la #FIELD MyField="BAR" - Il s'agit d'une valeur de contrôle que j'ajoute en récupérant d'autres informations sur les données au fur et à mesure que le fichier est construit (dans mon cas, il s'agit d'un indicateur de langue, tel que EN ou DE, ce qui donne littéralement #FIELD MyField="EN" Toute autre valeur dans ce champ serait ignorée, car il ne s'agit pas d'un enregistrement correspondant à mes critères.

Je pense que cela peut être fait en Awk ou en Perl, je peux faire des phrases très simples, mais cela dépasse mes compétences.

2voto

TLP Points 48922

Vous pourriez faire quelque chose comme ci-dessous. Ce n'est qu'une ébauche, mais elle fonctionnera avec votre échantillon de données. Utilisez le opérateur de bascule pour trouver le début et la fin des enregistrements. Utilisez un hachage pour stocker les valeurs des champs et un tableau pour stocker l'enregistrement.

Je vérifie simplement si la valeur se trouve dans la chaîne de caractères de l'emplacement. Vous pourriez souhaiter affiner la vérification en vous assurant qu'elle se trouve au bon endroit ou dans le bon cas.

use strict;
use warnings;

my @record;
my %f;
while(<DATA>) {
    if (/^#START / .. /^#END */) {
        if (/^#START /) {
            @record = (); # reset
            %f = ();
        }
        push @record, $_;
        if (/^#END */) { # check and print
            if ($f{'LOCATION'} !~ /$f{'MyField'}/) {
                print @record; 
            }
        } else {         # add fields to hash
            if (/^#FIELD (.+)/) {
                            # use split with limit of 2 fields
                my ($key, $val) = split /=/, $1, 2;
                next unless $val; # no empty values
                $val =~ s/^"|"$//g; # strip quotes
                $f{$key} = $val;
            }
        }
    }
}

__DATA__
#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor
#FIELD LOCATION=http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

2voto

Josh Y. Points 866

Un seul mot :

perl -ne 'BEGIN { $/ = "#END\n" }' -e '/MyField="(.*?)"/; print if !/Value=$1/' <file >newfile

Définit le Séparateur d'enregistrements d'entrée a "#END\n" perl lit donc les "chunks" dans $_ une à la fois, puis capture la valeur dans MyField et imprime le morceau entier si Value=$1 (c'est-à-dire la capture après "Value=") est no présent.

Vous pouvez bien sûr rendre les expressions rationnelles plus spécifiques si nécessaire.

2voto

jaypal Points 34440

Voici une petite gawk Une phrase pour vous -

gawk '
{
    if ($2!~/^#FIELD LOCATION/)
    {
        next;
    }
    else
    {
        split($2,ary,"=|&");
        split($4,ary1,"=|\"");
        if(ary[4]!=ary1[3])
            {
                print $0 > "badrec.file"
            }
    }
}' RS="#END\n" ORS="#END\n" FS="\n" file

Fichier d'entrée :

[jaypal:~/Temp] cat file
#START Descriptor # Good Record
#FIELD LOCATION="http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor # Bad Record
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor # Good Record
#FIELD LOCATION="http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

Test :

[jaypal:~/Temp] gawk '
{
    if ($2!~/^#FIELD LOCATION/)
    {
        next;
    }
    else
    {
        split($2,ary,"=|&");
        split($4,ary1,"=|\"");
        if(ary[4]!=ary1[3])
            {
                print $0 > "badrec.file"
            }
    }
}' RS="#END\n" ORS="#END\n" FS="\n" file

[jaypal:~/Temp] cat badrec.file 
#START Descriptor # Bad Record
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

0voto

Sorin Points 1662

Set (jeu de mots) séparateur d'enregistrements d'entrée a #END\n et lire directement les enregistrements :

#!/usr/bin/perl

$/ = "#END\n";

while (<DATA>) {
    next unless /^#FIELD LOCATION/m;
    /^#FIELD MyField="(.*)"$/m;
    next if /^#FIELD LOCATION.*$1/m;
    print
}

__DATA__
#START Descriptor
#FIELD LOCATION="http://path.to/file/here&Value=FOO&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END
#START Descriptor
#FIELD LOCATION=http://path.to/file/here&Value=BAR&OtherValue=BLAH"
#FIELD AnythingElse
#FIELD MyField="BAR"
#END

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X