Regex & Sed / Perl: Từ phù hợp mà trước đó không có từ khác


11

Tôi muốn sử dụng sedhoặc perlthay thế tất cả các lần xuất hiện của một từ không có từ nào đó trước từ đó.

Ví dụ: tôi có một tệp văn bản chứa cốt truyện của một bộ phim và tôi muốn thay thế tất cả các lần xuất hiện của tên nhân vật bằng tên của họ, nhưng chỉ khi tên của họ không xuất hiện ngay trước tên của họ.

Văn bản mẫu có thể trông như thế này:

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

Tôi muốn nó trông như thế này:

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

Nếu tôi chỉ làm sed 's/Smith/John/' file, thì tôi sẽ có:

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

Tên đầu tiên xuất hiện trước tên cuối cùng sẽ luôn giống nhau. Tôi không phải đối phó với John SmithFrank Smith. Tôi chỉ cần một cách để khớp với Smithcái không có Johntrước nó.


Bạn đang nói về sed nào?
Ignacio Vazquez-Abrams

GNU sed 4.2.1 trên Linux
jonescb

Câu trả lời:


8

Sẽ dễ dàng với bất kỳ ngôn ngữ nào trong đó các biểu thức chính quy có khả năng tìm kiếm. Tất nhiên, Perl là người đầu tiên trong danh sách:

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

Điểm yếu là có nhiều hơn một ký tự không lời giữa dòng chữ John John và cây Smith Smith. Thật không may một lượng hóa như +cho \Wsẽ tăng “chiều dài biến lookbehind không được thực hiện” lỗi.


6

EDIT .. là nhận xét của bạn .. Đây là một kịch bản mới không liên quan đến chính nó (ví dụ:) William Smith. Nó tạm thời làm xáo trộn các mẫu mà nó giữ là Smith (không thay đổi).

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

Nếu bạn quan tâm đến ông Mr Mr ... thì nó sẽ hoạt động.

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

Bạn có thể phục vụ cho William bằng cách thêm tên của anh ấy vào danh sách hoặc , ví dụ.
sed -r 's/\<(William|John|...


Đây là kịch bản ban đầu

sed -r 's/(^|[[:punct:]] |\<[a-z]+ )(Smith\>)/\1John/'

Điều này hoạt động, nhưng một vấn đề tôi tìm thấy là nếu từ trước Smith được viết hoa (ví dụ: nó xuất hiện sau từ đầu tiên trong câu) thì nó không khớp. Giải pháp perl bằng manatwork không có vấn đề đó, ngay cả khi nó sẽ thất bại trong các tình huống khác. May mắn thay, tệp văn bản của tôi không có tiêu đề như Mr. hoặc những người có cùng họ.
jonescb

Vâng cảm ơn ... Tôi đã đăng một kịch bản được trang bị ...
Peter.O

1
 sed -r 's/([^John] )Smith/\1John/g;s/([^Jane] )Johnson/\1Jane/g'

() Sẽ nắm bắt Tên không phải trước Tên cuối cùng, vì vậy chúng sẽ được sao lưu trong thay thế.

Biên tập

@ manatwork, gilles

Bạn đúng. Làm thế nào về

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

Điều này dường như để làm các mẹo.


Điều này sẽ thất bại nếu không có từ nào khác trước tên, ví dụ như Smith Smith và Jane Johnson nói về chiếc xe của Smith.
manatwork

1
[^John]phù hợp với một nhân vật mà phải là một trong J, o, hhoặc n. Tôi nghi ngờ đây là những gì bạn dự định. Không có cấu trúc phủ định trong các biểu thức thông thường (Perl có (?!…)(?<!…), nhưng nếu bạn nghĩ nó là một phủ định, có lẽ nó sẽ không làm những gì bạn mong đợi).
Gilles 'SO- ngừng trở nên xấu xa'

@Juaco: Take-2 của bạn hoạt động, nhưng dễ bị dữ liệu bất ngờ. Tôi đã sử dụng một phương pháp tương tự (mặc dù hơi miễn cưỡng) bởi vì sử dụng sedmà không có nó làm cho logic sed cồng kềnh ... temp1hầu như sẽ luôn ổn, nhưng! coi chừng xe buýt đó Để giảm thiểu khả năng này, tôi tin rằng tốt hơn là sử dụng các ký tự (hầu như) không bao giờ xảy ra trong các tệp văn bản Latin-Script, ví dụ: Hex value \ x01 \ x02 hoặc các kết hợp của chúng, hoặc có lẽ là \ xe188b4 UTF-8 locale (- XEM TRIỆU CHỨNG ETHIOPIC) .. vd. echo -e 'Z' |sed 's/./\xe1\x88\xb4/'=> khi miền địa phương là UTF-8 ..
Peter.O
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.