Regex & Sed / Perl: відповідне слово, якому НЕ передує інше слово


11

Я хотів би використати sedабо perlзамінити всі випадки слова, яке перед ним не має певного слова.

Наприклад, у мене є текстовий файл, який містить сюжет фільму, і я хочу замінити всі зустрічі прізвища персонажа на їх ім'я, але лише в тому випадку, якщо їх ім'я не з’явиться безпосередньо перед прізвищем.

Приклад тексту може виглядати приблизно так:

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

Я хочу, щоб це виглядало так:

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

Якби я це просто робив sed 's/Smith/John/' file, тоді я мав би:

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

Ім'я, яке постане перед прізвищем, завжди буде однаковим. Мені не доводиться мати справу John Smithі Frank Smith. Мені просто потрібен спосіб відповідності Smith, який не Johnпередував йому.


Про яку седу ви говорите?
Ігнасіо Васкес-Абрамс

GNU sed 4.2.1 на Linux
jonescb

Відповіді:


8

Буде легко з будь-якою мовою, де регулярні вирази здатні дивитися позаду. Звичайно, Perl є першим у списку:

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

Слабка сторона полягає в тому, що між "Джоном" і "Смітом" є не один несловесний символ. На жаль, кількісний показник, подібний +до, \Wпризведе до помилки "Перегляд змінної довжини позаду не реалізований".


6

EDIT .. знову ваш коментар .. Ось новий сценарій, який не стосується себе (напр.) Вільяма Сміта. Він тимчасово обтяжує візерунки, які він зберігає як Сміт (без змін).

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

Якщо ви турбуєтесь про містера місіс місіс ... то це спрацює.

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

Ви можете задовольнити Вільяма , додавши його ім’я до списку або , наприклад,
sed -r 's/\<(William|John|...


Це оригінальний сценарій

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

Це працює, але одна проблема, яку я знайшов, полягала в тому, що якщо слово перед Смітом пишеться з великої літери (наприклад, воно вводиться після першого слова в реченні), воно не відповідає. Розв’язання Perl у манаборстві не має такої проблеми, навіть якщо воно не вдасться в інших ситуаціях. На щастя, у моєму текстовому файлі немає назв, таких як містер або люди з таким же прізвищем.
jonescb

Так, дякую ... Я опублікував доповнений сценарій ...
Peter.O

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

() Захопить не-ім’я перед прізвищем, тому вони будуть замінені назад.

Редагувати

@ manatwork, gilles

Ти маєш рацію. Як на рахунок

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

Це, здається, робить трюк.


Це не вдасться, якщо перед назвою не буде іншого слова, наприклад "Сміт і Джейн Джонсон говорять про машину Сміта".
манастирство

2
[^John]відповідає один символ , який повинен бути один з J, o, hабо n. Сумніваюсь, це те, що ви задумали. Там немає заперечення конструкту в регулярних виразах (Perl є (?!…)і (?<!…), але якщо ви думаєте про нього , як заперечення, це , ймовірно , не робитиме те , що ви очікуєте).
Жил "ТАК - перестань бути злим"

@Juaco: Ваш номер 2 працює, але він чутливий до несподіваних даних. Я використовував подібний метод (хоч і трохи неохоче), оскільки використання sedбез нього сприймає роздуту логіку sed ... temp1майже завжди буде добре, але! стежте за тим автобусом. Щоб пом'якшити цю можливість, я вважаю, що краще використовувати символи, які (майже) ніколи не зустрічаються в текстових файлах з латинським сценарієм, наприклад, шістнадцяткове значення \ x01 \ x02 або їх комбінації, або, можливо, \ xe188b4 UTF-8 локалі (ሴ - ЕТІОПІЧНІ СИСТЕМИ ДИВАТИ) .. напр. echo -e 'Z' |sed 's/./\xe1\x88\xb4/'=> коли локальним є UTF-8 ..
Пітер.O
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.