У меня есть много таких фрагментов строк:
...lorem ipsumMYLINEBREAK01\r \r SURNAME, Name (LT)\r \r\nMYBREAK01lorem ipsum...
Это результат обработки большого HTML-файла с помощью rvest::html_text2(). Короче говоря — обрабатывать файл по узлам парсером xml2 громоздко — слишком много времени занимает. Если я удалю текст из HTML, в тексте появятся определенные закономерности, которыми можно воспользоваться. Например, я уже вставил заполнители MYBREAK01 и MYLINEBREAK01. Я немного запутался, пытаясь избавиться от ненужных \r и \n (возврат каретки и перевод строки, которые могут перемежаться пробелами - или, по крайней мере, кажутся пробелами).
Я попытался поставить %>% gsub() в цепочку обработки, которая должна избавиться от этих символов, но у меня проблемы с сопоставлением, и я не совсем понимаю, что делаю неправильно:
gsub("(MYLINEBREAK01)(\r|\r\n| |\n)+([a-zA-Z ()]+)(\r|\r\n| \n)+(MYBREAK01)","\\1\\3\\5",.)
но, похоже, это не соответствует тому, что я хочу - фрагмент строки остается неизменным. И вещь типа (LT) не всегда появляется в поле. Моя цель, конечно, получить строку MYLINEBREAK01SURNAME, Name (LT)MYBREAK01 — без (LT), если ее там нет.
Я понимаю, что, как предложили SamR и Margusl, мне следует использовать PCRE, и это очень полезно. Однако у меня также есть проблема: строка перемежается с другим текстом, и мне нужно ограничить эти изменения областью между маркерами MYLINEBREAK01 и MYBREAK01. Вот почему у меня были эти группы захвата. Иду ли я с ними в правильном направлении или я упускаю что-то очевидное?
Большое спасибо!
P.S. Причина, по которой я использую rvest::html_text2, заключается в том, что я готовлю данные для загрузки в созданную мной базу данных доступа — в ней есть несколько удобных функций поиска и фильтрации, которые помогают в анализе текста. Итак, для одного из полей я пытаюсь сохранить разрывы строк. При подготовке текста я вставляю 3 заполнителя BREAK и один заполнитель LINEBREAK, затем заменяю оставшиеся разрывы строк еще одним заполнителем 0mylinebreak0, считываю это в фрейм данных, заменяю 0mylinebreak0 на \n с помощью stringr, а затем сохраняю в CSV. Какой доступ затем читается успешно, и у меня есть база данных из примерно 5 тысяч записей, которую я могу использовать дальше. Звучит громоздко, но это лучше, чем ручная обработка данных.
Теперь у вопроса есть ответы, я думаю, было бы лучше отменить его и задать другой вопрос, указав другую проблему, а не редактировать, чтобы сделать ответы недействительными.
Привет, СэмР. Я никогда не хотел сказать, что ответы недействительны. Я задал вопрос, пытаясь узнать, и думаю, что недостаточно хорошо описал свою ситуацию.





Если отбросить на минутку тот факт, что вы каждый раз, когда вы анализируете html с помощью регулярного выражения, это вызывает испорченные души в царство живых,
Я думаю, сейчас подходящее время для PCRE \v. Как говорится в документах:
\vсоответствует любому символу, который считается вертикальным пробелом; сюда входят символы возврата каретки и перевода строки (новая строка), а также несколько других символов, перечисленных в таблице ниже.
x <- "MYLINEBREAK01\r \r SURNAME, Name (LT)\r \r\nMYBREAK01"
gsub("\\v", "", x, perl = TRUE)
# [1] "MYLINEBREAK01 SURNAME, Name (LT) MYBREAK01"
Похоже, вы хотите сделать что-то странное с интервалом. Мы можем использовать отрицательный просмотр назад, чтобы удалить все пробелы, если им не предшествует запятая. Как вы сказали, вы не совсем уверены, что это пробелы, я использовал PCRE \h:
\hсоответствует любому символу, который считается горизонтальным пробелом; сюда входят символы пробела и табуляции платформы, а также некоторые другие.
gsub("\\v", "", x, perl = TRUE) |>
gsub("(?<!,)\\h", "", x = _, perl = TRUE)
# [1] "MYLINEBREAK01SURNAME, Name(LT)MYBREAK01"
\h и \v будут соответствовать следующим символам, похожим на пробелы. Большинству из них не соответствует \s в R (хотя согласно спецификации PCRE они должны быть):
0x0009 CHARACTER TABULATION h s
0x000a LINE FEED (LF) vs
0x000b LINE TABULATION vs [1]
0x000c FORM FEED (FF) vs
0x000d CARRIAGE RETURN (CR) vs
0x0020 SPACE h s
0x0085 NEXT LINE (NEL) vs [2]
0x00a0 NO-BREAK SPACE h s [2]
0x1680 OGHAM SPACE MARK h s
0x2000 EN QUAD h s
0x2001 EM QUAD h s
0x2002 EN SPACE h s
0x2003 EM SPACE h s
0x2004 THREE-PER-EM SPACE h s
0x2005 FOUR-PER-EM SPACE h s
0x2006 SIX-PER-EM SPACE h s
0x2007 FIGURE SPACE h s
0x2008 PUNCTUATION SPACE h s
0x2009 THIN SPACE h s
0x200a HAIR SPACE h s
0x2028 LINE SEPARATOR vs
0x2029 PARAGRAPH SEPARATOR vs
0x202f NARROW NO-BREAK SPACE h s
0x205f MEDIUM MATHEMATICAL SPACE h s
0x3000 IDEOGRAPHIC SPACE h s
Дополнительную информацию см. в документации PCRE.
Отлично, спасибо, попробую. Я использовал html_text2, чтобы избавиться от html перед регулярными выражениями именно для того, чтобы не вызывать нечестивых духов.
Я обновил свой вопрос, объясняя, почему я пытался использовать группы захвата, поскольку строка встроена в текст, который я не хочу изменять. Не могли бы вы взглянуть на это, может быть, есть способ изменить эту строку, не затрагивая остальной текст?
Большое спасибо, отличное объяснение и отличный ответ. Извиняюсь за то, что изначально не был очень точен в своем вопросе, я должен был сказать, что основная проблема заключалась в том, как обращаться со строками, встроенными в текст.
str_replace_all() с просмотром назад/просмотром в регулярном выражении и с функцией замены.
library(stringr)
s_ <- "...lorem \n ipsumMYLINEBREAK01\r \r SURNAME, Name (LT)\r \r\nMYBREAK01lorem \n ipsum..."
s_ <- str_c(s_, s_)
str_view(s_)
#> [1] │ ...lorem
#> │ ipsumMYLINEBREAK01{\r} {\r} SURNAME, Name (LT){\r} {\r}
#> │ MYBREAK01lorem
#> │ ipsum......lorem
#> │ ipsumMYLINEBREAK01{\r} {\r} SURNAME, Name (LT){\r} {\r}
#> │ MYBREAK01lorem
#> │ ipsum...
# minimum number of whitespace and non-whitespace characters between
# positive lookbehind and lookahead patterns,
# match gets first passed though str_squish() and then is used for replacement
s_2 <- str_replace_all(s_, "(?<=MYLINEBREAK01)[\\s\\S]*?(?=MYBREAK01)", str_squish)
str_view(s_2)
#> [1] │ ...lorem
#> │ ipsumMYLINEBREAK01SURNAME, Name (LT)MYBREAK01lorem
#> │ ipsum......lorem
#> │ ipsumMYLINEBREAK01SURNAME, Name (LT)MYBREAK01lorem
#> │ ipsum...
Звучит как работа для stringr::str_squish(), что по сути является причудливым названием для gsub("\\s+", " ", s_) |> trimws().
s_ <- "MYLINEBREAK01\r \r SURNAME, Name (LT)\r \r\nMYBREAK01"
stringr::str_squish(s_)
#> [1] "MYLINEBREAK01 SURNAME, Name (LT) MYBREAK01"
# which is basically
gsub("\\s+", " ", s_) |> trimws()
#> [1] "MYLINEBREAK01 SURNAME, Name (LT) MYBREAK01"
rvest примечание: возможно, вам здесь нужен html_text() (sans-2), и подход к этой проблеме с помощью rvest с помощью комбинации селекторов CSS и/или XPath (и, возможно, некоторых xml2 трюков для некоторых крайних случаев), вероятно, упростит вашу задачу.
Спасибо, это работает очень хорошо, и я узнал кое-что об использовании str_replace_all(,,). Я полагаю, что если вы используете группы захвата в параметре match, то вы можете ссылаться на них при замене через "\\1" и т. д., как в gsub()?
Интересный сценарий, но боюсь, вы не сможете ссылаться на группы захвата в функции замены, из ?str_replace : replacement /../ "В качестве альтернативы укажите функцию, которая будет вызываться один раз для каждого совпадения (справа налево) и его возвращаемое значение будет использоваться для замены совпадения».
Спасибо, интересно, это действительно работает: s_ <- "...lorem ipsumMYLINEBREAK01 SURNAME, Name (LT) 0 MYBREAK01lorem ipsum..." а потом s_ %>% str_replace_all(., "(?<=MYLINEBREAK01).*(SURNAME).*(Name).*(?=MYBREAK01)", " \\2 \\1 works") дает "...lorem ipsumMYLINEBREAK01 Name SURNAME worksMYBREAK01lorem ipsum..."
Да, это ожидаемо. Я почему-то подумал, что ваш вопрос касается использования функции в качестве третьего str_replace_all() параметра. Просмотр назад/просмотр в регулярном выражении работает и с gsub().
gsub('\\v', '', x, perl = TRUE)подходит вам?