Regex & Sed / Perl: correspond au mot qui N'EST PAS précédé d'un autre mot

11

Je voudrais utiliser sedou perlremplacer toutes les occurrences d'un mot qui n'a pas un certain mot devant lui.

Par exemple, j'ai un fichier texte qui contient une intrigue d'un film et je veux remplacer toutes les occurrences du nom de famille d'un personnage par leur prénom, mais seulement si leur prénom ne vient pas immédiatement avant leur nom de famille.

Un exemple de texte pourrait ressembler à ceci:

John Smith and Jane Johnson talk about Smith's car.

Je veux que ça ressemble à ceci:

John Smith and Jane Johnson talk about John's car.

Si je le fais sed 's/Smith/John/' file, j'aurais:

John John and Jane Johnson talk about John's car.

Le prénom qui précède le nom de famille sera toujours le même. Je n'ai pas à gérer John Smithet Frank Smith. J'ai juste besoin d'un moyen de faire correspondre Smithcelui qui ne l'a pas Johnprécédé.

jonescb
la source
De quel sed parles-tu?
Ignacio Vazquez-Abrams
GNU sed 4.2.1 sur Linux
jonescb

Réponses:

8

Ce serait facile avec n'importe quelle langue où les expressions régulières sont capables de regarder derrière. Bien sûr, Perl est le premier sur la liste:

perl -pe 's/(?<!John\W)Smith/John/g' <<< "John Smith and Jane Johnson talk about Smith's car."

Le point faible est d'avoir plus d'un caractère non-mot entre «John» et «Smith». Malheureusement, un quantificateur tel que +for \Wsoulèverait l'erreur «Lookbehind de longueur variable non implémenté».

homme au travail
la source
6

EDIT .. re votre commentaire .. Voici un nouveau script qui ne se préoccupe pas (par exemple.) De William Smith. Il obscurcit temporairement les motifs qu'il garde comme Smith (inchangé).

sed -r 's/\<(John) (Smith)\>/\1\x01x\2/g; 
        s/\<Smith\>/John/g;  s/\x01x/ /g'

Si vous êtes préoccupé par M. Mr Mme ... alors cela fonctionne.

sed -r 's/\<(John|((M(r|rs|s))\.?)) (Smith)\>/\1\x01x\5/g
        s/\<Smith\>/John/g; s/\x01x/ /g'

Vous pouvez répondre à William en ajoutant son nom à la liste ou , par exemple.
sed -r 's/\<(William|John|...


Ceci est le script d'origine

sed -r 's/(^|[[:punct:]] |\<[a-z]+ )(Smith\>)/\1John/'
Peter.O
la source
Cela fonctionne, mais le seul problème que j'ai trouvé est que si le mot avant Smith est en majuscule (par exemple, il vient après le premier mot d'une phrase), il ne correspond pas. La solution perl par manatwork n'a pas ce problème, même si elle échouerait dans d'autres situations. Heureusement, mon fichier texte n'a pas de titres comme M. ou des personnes du même nom.
jonescb
Oui merci ... J'ai posté un script modifié ...
Peter.O
1
 sed -r 's/([^John] )Smith/\1John/g;s/([^Jane] )Johnson/\1Jane/g'

Le () capturera le non-prénom avant un nom de famille, donc ils sont rétro-réfutés dans le remplacement.

Éditer

@ manatwork, gilles

Vous avez raison. Que diriez-vous

sed -r 's/(John Smith)/temp1/g;s/Smith/John/g;s/temp1/John Smith/g'

Cela semble faire l'affaire.

à
la source
Cela échouera s'il n'y a pas d'autre mot avant le nom, par exemple «Smith et Jane Johnson parlent de la voiture de Smith».
manatwork
2
[^John]correspond à un caractère qui doit être l' un J, o, hou n. Je doute que c'est ce que vous vouliez. Il n'y a pas de construction de négation dans les expressions régulières (Perl a (?!…)et (?<!…), mais si vous le considérez comme une négation, il ne fera probablement pas ce que vous attendez).
Gilles 'SO- arrête d'être méchant'
@Juaco: Votre take-2 fonctionne, mais est susceptible de recevoir des données inattendues. J'ai utilisé une méthode similaire (quoique un peu à contrecœur) car l'utiliser sedsans elle rend la logique sédative gonflée ... ce temp1sera presque toujours bien, mais! attention à ce bus. Pour atténuer cette possibilité, je pense qu'il est préférable d'utiliser des caractères qui (presque) ne se produisent jamais dans les fichiers texte en Latin-Script, par exemple la valeur Hex \ x01 \ x02, ou des combinaisons d'entre eux, ou peut-être \ xe188b4 locale UTF-8 (ሴ - ETHIOPIC SYLLABLE SEE) .. ex. echo -e 'Z' |sed 's/./\xe1\x88\xb4/'=> lorsque les paramètres régionaux sont UTF-8 ..
Peter.O