R: Запутался в замене символов \r с помощью gsub

У меня есть много таких фрагментов строк:

...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 тысяч записей, которую я могу использовать дальше. Звучит громоздко, но это лучше, чем ручная обработка данных.

gsub('\\v', '', x, perl = TRUE) подходит вам?

rawr 23.04.2024 20:03

Теперь у вопроса есть ответы, я думаю, было бы лучше отменить его и задать другой вопрос, указав другую проблему, а не редактировать, чтобы сделать ответы недействительными.

SamR 24.04.2024 08:43

Привет, СэмР. Я никогда не хотел сказать, что ответы недействительны. Я задал вопрос, пытаясь узнать, и думаю, что недостаточно хорошо описал свою ситуацию.

r0berts 25.04.2024 09:49
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
3
116
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если отбросить на минутку тот факт, что вы каждый раз, когда вы анализируете 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 перед регулярными выражениями именно для того, чтобы не вызывать нечестивых духов.

r0berts 23.04.2024 20:31

Я обновил свой вопрос, объясняя, почему я пытался использовать группы захвата, поскольку строка встроена в текст, который я не хочу изменять. Не могли бы вы взглянуть на это, может быть, есть способ изменить эту строку, не затрагивая остальной текст?

r0berts 24.04.2024 08:03

Большое спасибо, отличное объяснение и отличный ответ. Извиняюсь за то, что изначально не был очень точен в своем вопросе, я должен был сказать, что основная проблема заключалась в том, как обращаться со строками, встроенными в текст.

r0berts 25.04.2024 19:35
Ответ принят как подходящий
обновление для решения вопроса обновления:

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()?

r0berts 25.04.2024 19:33

Интересный сценарий, но боюсь, вы не сможете ссылаться на группы захвата в функции замены, из ?str_replace : replacement /../ "В качестве альтернативы укажите функцию, которая будет вызываться один раз для каждого совпадения (справа налево) и его возвращаемое значение будет использоваться для замены совпадения».

margusl 25.04.2024 19:58

Спасибо, интересно, это действительно работает: 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..."

r0berts 27.04.2024 08:32

Да, это ожидаемо. Я почему-то подумал, что ваш вопрос касается использования функции в качестве третьего str_replace_all() параметра. Просмотр назад/просмотр в регулярном выражении работает и с gsub().

margusl 27.04.2024 09:14

Другие вопросы по теме

Похожие вопросы