Замените пробелы между несколькими (3+) заглавными буквами
У меня есть текст, в котором люди используют капиталы с пробелами между ними, чтобы сделать выдачу подстроки. Я хочу заменить пробелы между этими подстроками. Правила для шаблона: "по крайней мере 3 последовательных заглавных буквы с пробелом между каждой буквой".
Мне любопытно, как это сделать с чистым регулярным выражением, но также с пакетом gsubfn, поскольку я думал, что это будет легкой задачей для него, но в примере MWE ниже я разбился и сжег, когда там было добавлено дополнительное письмо (I Любопытно, почему это происходит).
MWE
x <- c(
'Welcome to A I: the best W O R L D!',
'Hi I R is the B O M B for sure: we A G R E E indeed.'
)
## first to show I have the right regex pattern
gsub('(([A-Z]\\s+){2,}[A-Z])', '<FOO>', x)
## [1] "Welcome to A I: the best <FOO>!"
## [2] "Hi I R is the <FOO> for sure: we <FOO> indeed."
library(gsubfn)
spacrm1 <- function(string) {gsub('\\s+', '', string)}
gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm1, x)
## Error in (function (string) : unused argument ("L ")
## "Would love to understand why this error is happening"
spacrm2 <- function(...) {gsub('\\s+', '', paste(..., collapse = ''))}
gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm2, x)
## [1] "Welcome to A I: the best WORLDL!"
## [2] "Hi I R is the BOMBM for sure: we AGREEE indeed."
## "Would love to understand why the extra letter is happening"
Желаемый выход
[1] "Welcome to A I: the best WORLD!"
[2] "Hi I R is the BOMB for sure: we AGREE indeed."
Ответы
Ответ 1
Как я уже отмечал в комментариях, проблема в первом вызове gsubfn в вопросе возникает из-за того, что в регулярном выражении есть две группы захвата, но только один аргумент функции. Они должны совпадать - две группы захвата подразумевают необходимость в двух аргументах. Мы можем видеть, что gsubfn проходит, запустив это и просмотрев вывод инструкции print:
junk <- gsubfn('(([A-Z]\\s+){2,}[A-Z])', ~ print(list(...)), x)
Мы можем решить эту проблему любым из следующих способов:
1) Это использует регулярное выражение из вопроса, но использует функцию, которая принимает несколько аргументов. Фактически используется только первый аргумент.
gsubfn('(([A-Z]\\s+){2,}[A-Z])', ~ gsub("\\s+", "", ..1), x)
## [1] "Welcome to A I: the best WORLD!"
## [2] "Hi I R is the BOMB for sure: we AGREE indeed."
Обратите внимание, что он интерпретирует формулу как функцию:
function (...) gsub("\\s+", "", ..1)
Мы можем просмотреть функцию, сгенерированную по формуле:
fn$identity( ~ gsub("\\s+", "", ..1) )
## function (...)
## gsub("\\s+", "", ..1)
2) Это использует регулярное выражение из вопроса, а также функцию из вопроса, но добавляет аргумент backref = -1, который говорит ему передать только первую группу захвата функции - минус означает, что и весь матч не проходит.
gsubfn('(([A-Z]\\s+){2,}[A-Z])', spacrm1, x, backref = -1)
(Как указывает @Wiktor Stribiżew в его ответе backref=0
).
3) Другой способ выразить это, используя регулярное выражение, состоит в следующем:
gsubfn('(([A-Z]\\s+){2,}[A-Z])', x + y ~ gsub("\\s+", "", x), x)
Обратите внимание, что он интерпретирует формулу как эту функцию:
function(x, y) gsub("\\s+", "", x)
Ответ 2
Краткое
В R есть способ сделать это, используя regex целиком, но это не очень (хотя я думаю, что это выглядит довольно мило!) Этот ответ также настраивается независимо от ваших потребностей (два заглавных минимума, три минимума и т.д. ) - то есть масштабируемый - и может соответствовать нескольким горизонтальным символам пробела (не использует lookbehinds, для которых требуется фиксированная ширина).
код
См. здесь выражение regex
(?:(?=\b(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu})
Замена: пустая строка
Изменить
Я только понял, что здесь использовал \b
, который, возможно, не работает с символами Unicode (например, É
). Следующий вариант, вероятно, является лучшим подходом. Он проверяет, что предшествующий первый символ верхнего регистра не является буквой (с любого языка /script). Он также гарантирует, что он не соответствует заглавному символу в конце строки верхнего регистра, если за ним следует любая другая буква.
Если вам также необходимо обеспечить, чтобы числа не предшествовали заглавным буквам, вы можете использовать [^\p{L}\p{N}]
вместо \P{L}
.
См. здесь выражение regex
(?:(?<=\P{L})(?=(?:\p{Lu}\h+){2}\p{Lu})|\G(?!\A))\p{Lu}\K\h+(?=\p{Lu}(?!\p{L}))
Использование
Смотрите код, используемый здесь
x <- c(
"Welcome to A I: the best W O R L D!",
"Hi I R is the B O M B for sure: we A G R E E indeed."
)
gsub("(?:(?=\\b(?:\\p{Lu}\\h+){2}\\p{Lu})|\\G(?!\\A))\\p{Lu}\\K\\h+(?=\\p{Lu})", "", x, perl=TRUE)
Результаты
Ввод
Welcome to A I: the best W O R L D!
Hi I R is the B O M B for sure: we A G R E E indeed.
Выход
Welcome to A I: the best WORLD!
Hi I R is the BOMB for sure: we AGREE indeed.
Описание
-
(?:(?=(?:\b\p{Lu}\h+){2}\p{Lu})|\G(?!\A))
Соответствует любому из следующих
-
(?=\b(?:\p{Lu}\h+){2}\p{Lu})
Положительный lookahead, обеспечивающий соответствие совпадений (используется в качестве подтверждения в этом случае для поиска всех местоположений в строке, которые находятся в формате A A A
). Вы также можете добавить \b
в конце этого положительного вида, чтобы убедиться, что что-то вроде I A Name
не соответствует
-
\b
Позиция подтверждения на границе слова
-
(?:\p{Lu}\h+){2}
Совместите следующее ровно дважды
-
\p{Lu}
Совместить символ верхнего регистра на любом языке (Unicode)
-
\h+
Соответствует одному или нескольким горизонтальным символам пробела
-
\p{Lu}
Совместить символ верхнего регистра на любом языке (Unicode)
-
\G(?!\A)
Позиция подтверждения в конце предыдущего совпадения
-
\p{Lu}
Совместить символ верхнего регистра на любом языке (Unicode)
-
\K
Сбрасывает начальную точку сообщенного совпадения. Любые ранее употребляемые символы больше не включаются в окончательное соответствие
-
\h+
Соответствует одному или нескольким горизонтальным символам пробела
-
(?=\p{Lu})
Положительный взгляд, обеспечивающий то, что следует за символом верхнего регистра на любом языке (Unicode)
Ответ 3
Проблема заключается в том, какие элементы передаются spacrm
функциями gsubfn
и несоответствие числа аргументов spacrm
function accept и количество переданных им аргументов.
Смотрите gsubfn
docs о аргументе backref
:
Число обратных ссылок, которые нужно передать функции. Если нуль или положительное совпадение передается как первый аргумент функции замены, за которым следует указанное количество обратных ссылок в качестве последующих аргументов. Если отрицательно, тогда передается только то количество обратных ссылок, но не соответствует самому совпадению. Если он опущен, он будет определен автоматически, то есть будет 0, если нет обратных ссылок и , иначе это будет равное отрицательное количество обратных ссылок. Он определяет это, подсчитывая количество неэкспериментированных левых скобок в шаблоне.
Итак, в вашем случае аргумент backref
был опущен, а spacrmX
функции получили W O R L D
и L
значения.
Функция spacrm1
, которая принимает только один аргумент, получила два аргумента, поэтому ошибка unused argument ("L ")
.
Когда использовался spacrm2
, он получил все два захваченных значения, и они были объединены (после удаления пробелов).
Фактически вы можете просто использовать backref=0
, чтобы сообщить gsubfn
обрабатывать только полное значение соответствия и упростить шаблон, удалить группы захвата и вместо этого использовать один не захват:
spacrm1 <- function(string) {gsub('\\s+', '', string)}
x <- c(
'Welcome to A I: the best W O R L D!',
'Hi I R is the B O M B for sure: we A G R E E indeed.'
)
gsubfn('(?:[A-Z]\\s+){2,}[A-Z]', spacrm2, x, backref=0)
[1] "Welcome to A I: the best WORLD!"
[2] "Hi I R is the BOMB for sure: we AGREE indeed."
Ответ 4
Вы можете просто сопоставить пробел, которому предшествует прописная буква, а также две заглавные буквы, разделенные пробелом (с использованием look-around). или наоборот - сопоставить пробел, которому предшествуют две заглавные буквы, разделенные пробелом, а затем заглавная буква.
(?<=[A-Z]) (?=[A-Z] [A-Z])|(?<=[A-Z] [A-Z]) (?=[A-Z])
R-код:
x <- c(
"Welcome to A I: the best W O R L D!",
"Hi I R is the B O M B for sure: we A G R E E indeed."
)
gsub("(?<=[A-Z]) (?=[A-Z] [A-Z])|(?<=[A-Z] [A-Z]) (?=[A-Z])", "", x, perl=TRUE)
Живите здесь, в ideone.