J'essaie d'extraire les intitulés de poste les mieux rémunérés de cet exemple de texte:
my @arr_found = ($content =~ m/"$regex"/g);
en utilisant l'expression régulière et le code Perl suivants.
use File::Glob; local $/ = undef; my $file = @ARGV[0]; open INPUT, "<", $file or die "Couldn't open file $!\n"; my $content = <INPUT>; my $regex = "^\w+(\w+)*$\n\n#(\d+)"; my @arr_found = ($content =~ m/^\w+(\w+)*$\n\n#(\d+)/g); close (INPUT);
3 Réponses :
Pourquoi ne pas traiter ligne par ligne, simple et facile
say "#$_ $jobs{$_}" for sort { $a <=> $b } keys %jobs;
Cela repose sur l'exigence que la ligne #N
soit la première ligne non vide après le titre du poste.
Il imprime
my %jobs; while ($content =~ /$regex/g) { $jobs{$2} = $1; }
La question ne dit pas si les classements sont également recherchés, mais il y a un indice dans l'expression régulière ils peuvent être. Ensuite, en supposant que l'ordre dans le fichier est "correct", vous pouvez parcourir les indices du tableau et imprimer les éléments (titres) avec leurs indices (rang).
Ou, pour être sûr, capturez-les dans l'expression régulière, / ^ \ s * # ([0-9] +) /
. Ensuite, vous pouvez imprimer directement à la fois le titre et son rang, ou peut-être les stocker dans un hachage avec des paires clé-valeur rank => title
.
Comme pour l'expression régulière , il y a quelques corrections nécessaires. Pour composer une regex avant la mise en correspondance, ce qui est une bonne idée, vous voulez le opérateur qr . Pour travailler avec des chaînes multilignes, vous avez besoin du modificateur / m
. (Voir perlretut .) L'expression régulière elle-même doit être corrigée. Par exemple,
my $regex = qr/^(.+)?(?:\n\s*)+\n\s*#\s*([0-9]+)/m; my %jobs = reverse $content =~ /$regex/g;
ce qui capture une ligne suivie d'au moins une ligne vide, puis #N
sur une autre ligne.
Si le classement des titres est également nécessaire, capturez-le également et stockez-le dans un hachage
my $regex = qr/^(.+)?(?:\n\s*)+\n\s*#\s*[0-9]/m; my @titles = $content =~ /$regex/g
ou mieux vaut ne pas le pousser avec reverse
-ing la liste des correspondances mais itérer à travers des paires à la place
Data Scientist Programmer SAP Module Consultant
car avec cela nous pouvons vérifier notre "catch" à chaque itération, faire d'autres traitements, etc. Ensuite, vous pouvez trier les clés pour imprimer dans l'ordre
use warnings; use strict; use feature 'say'; my $file = shift || die "Usage: $0 file\n"; open my $fh, '<', $file or die "Can't open $file: $!"; my (@jobs, $prev_line); while (my $line = <$fh>) { chomp $line; next if not $line =~ /\S/; if ($line =~ /^\s*#[0-9]/) { push @jobs, $prev_line; } $prev_line = $line; } say for @jobs;
et juste en général choisir les emplois en fonction de leur rang si nécessaire.
Je pense qu'il est juste de dire que l'expression régulière ici est beaucoup plus complexe que le premier programme.
Merci pour la réponse. eh bien, je voulais traiter le texte entier comme une seule chaîne parce que ce n'était pas un texte très long et je voulais voir si je pouvais l'aborder avec une seule expression régulière.
@Romario Bien sûr, c'est comme ça que nous apprenons :) Je voulais seulement proposer une approche plus simple (en principe cruciale en production et avec des problèmes difficiles: la décomposer en utilisant des approches informatiques et des algorithmes adaptés, pour simplifier), pas pour décourager. J'ai ajouté une section sur regex, par souci d'exhaustivité.
Vous ne preniez pas en compte les espaces (comme dans Data Scientist
):
^\w+.*$\R+#(\d+)
Voir une démo sur regex101.com .
\R
est égal à (?> \ r \ n | \ n | \ r | \ f | \ x0b | \ x85) code> (correspond aux séquences de retours à la ligne Unicode).
Merci pour la réponse. C'est une bonne regex. Mais il ne contenait pas comment transporter le jeu de résultats de l'expression régulière dans le tableau Perl, donc je ne pouvais pas le sélectionner avec une coche.
@Romario: Pas de soucis, heureux d'aider.
Réponses à vos questions:
vous capturez uniquement le deuxième mot et vous ne laissez pas d'espace entre eux. C'est pourquoi cela ne correspondra pas, par exemple Data Scientist
utilisez l'opérateur qr //
pour compiler des expressions rationnelles avec un contenu dynamique. L'erreur provient du $
au milieu de l'expression régulière que le compilateur de regex Perl suppose que vous vous êtes trompé, car $
ne devrait venir qu'à la fin d'une expression régulière. P >
Le code suivant devrait réaliser ce que vous voulez. Notez l'approche en deux étapes:
Rechercher le texte correspondant
^
) \ w + (?: \ s + \ w +) *
, pas besoin de capturer la correspondance) \ n \ n
) #
suivi d'un nombre ( \ d +
) / g
) et traiter les chaînes comme plusieurs lignes ( / m
, c'est-à-dire que ^
correspondra à tout début d'une ligne dans le texte d'entrée) Diviser la correspondance aux fins de ligne ( \ n
) et extraire le 1er et le 3ème champ
$ match
contiendra trois lignes, cette approche est beaucoup plus simple que d'écrire une autre expression régulière. my $regex = qr/^(\w+(?:\s+\w+)*\R\R#\d+)/m; ... my($title, undef, $rank) = split(/\R/, $match);
Test exécuté sur l'exemple de texte que vous avez fourni dans votre question.
$ perl dummy.pl dummy.txt MATCH 'Data Scientist' '1' MATCH 'Programmer' '2' MATCH 'SAP Module Consultant' '3'
MISE À JOUR UNICODE : comme suggéré par la réponse de @ Jan, le code peut être amélioré comme ceci:
#!/usr/bin/perl use strict; use warnings; use feature qw(say); use File::Slurper qw(read_text); my $input = read_text($ARGV[0]) or die "slurp: $!\n"; my $regex = qr/^(\w+(?:\s+\w+)*\n\n#\d+)/m; foreach my $match ($input =~ /$regex/g) { #say $match; my($title, undef, $rank) = split("\n", $match); $rank =~ s/^#//; say "MATCH '${title}' '${rank}'"; } exit 0;
C'est probablement l'approche la plus générique, comme UTF-8 est la valeur par défaut pour
File :: Slurper :: read_text ()
de toute façon ...
Merci pour la réponse. J'avais entendu parler du qr //
mais je l'ai totalement oublié. La partie boucle est sympa. Jamais vu ou utilisé la fonction use qw (disons);
et utilisent File :: Slurper qw (read_text);
avant mais ils sont très pratiques et très bien.
à l'intérieur de l'expression (?: \ s + \ w +) *
il y a :
J'ai essayé de regexing sans elle et cela n'a pas changé la sortie. Serait-ce une faute de frappe? Qu'est ce que ça fait?
Voir perlre : " (?: Pattern)
Ceci est pour le clustering, pas pour la capture; il regroupe des sous-expressions comme ()
, mais ne fait pas de références arrière comme le fait ()
"AKA non-capturing group (NCG). Lorsque vous devez regrouper des éléments, mais pas capturer le contenu, cela est préférable pour une légère amélioration des performances.
Je pensais à :
séparément, mais il s'est avéré être la combinaison ?:
et cela a du sens maintenant. Je vous remercie.
Q2: utilisez l'opérateur
qr //
(voir perlop ) pour compiler des expressions rationnelles. C'est à dire.mon $ regex = qr / ^ \ w + (\ w +) * $ \ n \ n # (\ d +) /;
. Vous pouvez également combiner des sous-expressions régulières de cette manière, c'est-à-dire.mon $ regex = qr / $ {subre1} $ {subre2} $ {subre3} /;
Q1 est un peu flou. Par ex. le premier exemple: voulez-vous faire correspondre
Data Scientist
et# 1
?