Regex & Sed / Perl: Mencocokkan kata yang TIDAK didahului dengan kata lain

11

Saya ingin menggunakan sedatau perlmengganti semua kemunculan kata yang tidak memiliki kata tertentu di depannya.

Misalnya, saya memiliki file teks yang berisi plot film dan saya ingin mengganti semua kemunculan nama belakang karakter dengan nama depan mereka, tetapi hanya jika nama depan mereka tidak muncul tepat sebelum nama belakang mereka.

Contoh teks mungkin terlihat seperti ini:

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

Saya ingin terlihat seperti ini:

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

Jika saya melakukannya sed 's/Smith/John/' file, maka saya akan memiliki:

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

Nama depan yang muncul sebelum nama belakang akan selalu sama. Saya tidak harus berurusan dengan John Smithdan Frank Smith. Saya hanya perlu cara untuk mencocokkan Smithyang tidak ada Johnsebelumnya.

jonescb
sumber
Sed yang kamu bicarakan?
Ignacio Vazquez-Abrams
GNU sed 4.2.1 di Linux
jonescb

Jawaban:

8

Akan mudah dengan bahasa apa pun di mana ekspresi reguler mampu terlihat di belakang. Tentu saja, Perl adalah yang pertama dalam daftar:

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

Kelemahannya adalah memiliki lebih dari satu karakter non-kata antara "John" dan "Smith". Sayangnya quantifier seperti +for \Wakan memunculkan error “Variable length lookbehind not implemented”.

manatwork
sumber
6

Sunting .. beri komentar Anda .. Berikut adalah skrip baru yang tidak memedulikan (mis.) William Smith. Itu sementara mengaburkan pola yang disimpan sebagai Smith (tidak berubah).

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

Jika Anda khawatir tentang Tn. Nyonya ... maka ini berhasil.

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

Anda dapat melayani William dengan menambahkan namanya ke daftar atau , misalnya.
sed -r 's/\<(William|John|...


Ini adalah naskah asli

sed -r 's/(^|[[:punct:]] |\<[a-z]+ )(Smith\>)/\1John/'
Peter.O
sumber
Ini berfungsi, tetapi satu masalah yang saya temukan adalah bahwa jika kata sebelum Smith ditulis dengan huruf kapital (mis. Kata itu muncul setelah kata pertama dalam sebuah kalimat) maka kata itu tidak cocok. Solusi perl oleh manatwork tidak memiliki masalah itu, bahkan jika itu akan gagal dalam situasi lain. Untungnya, file teks saya tidak memiliki judul seperti Mr. atau orang dengan nama belakang yang sama.
jonescb
Ya, terima kasih ... Saya telah mengirim naskah yang
diubah
1
 sed -r 's/([^John] )Smith/\1John/g;s/([^Jane] )Johnson/\1Jane/g'

The () akan menangkap non-Firstname sebelum LastName, sehingga mereka ditinjau kembali dalam penggantian.

Edit

@ manatwork, gilles

Kamu benar. Bagaimana tentang

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

Ini sepertinya berhasil.

ata
sumber
Ini akan gagal jika tidak ada kata lain sebelum nama, misalnya "Smith dan Jane Johnson berbicara tentang mobil Smith."
manatwork
1
[^John]cocok dengan salah satu karakter yang harus menjadi salah satu J, o, hatau n. Saya ragu ini yang Anda maksudkan. Tidak ada konstruk negasi dalam ekspresi reguler (Perl telah (?!…)dan (?<!…), tetapi jika Anda menganggapnya sebagai negasi, itu mungkin tidak akan melakukan apa yang Anda harapkan).
Gilles 'SANGAT berhenti menjadi jahat'
@ Joaco: Take-2 Anda berfungsi, tetapi rentan terhadap data yang tidak terduga. Saya menggunakan metode yang sama (walaupun agak enggan) karena menggunakan sedtanpa itu membuat logika sed bengkak ... temp1hampir selalu baik-baik saja, tapi! hati-hati dengan bus itu. Untuk mengurangi kemungkinan ini, saya percaya lebih baik menggunakan karakter yang (hampir) tidak pernah muncul dalam file teks Latin-Script, misalnya nilai Hex \ x01 \ x02, atau kombinasi dari mereka, atau mungkin \ xe188b4 UTF-8 lokal (ሴ - MELIHAT SINGKAT ETHIOPIC) .. mis. echo -e 'Z' |sed 's/./\xe1\x88\xb4/'=> ketika lokalnya adalah UTF-8 ..
Peter.O