Разделить вектор символа на знаках сравнения math в R
Я хотел бы разделить выражение с математическими сравнениями, например
unlist(strsplit("var<3", "(?=[=<>])", perl = TRUE))
unlist(strsplit("var==5", "(?=[=<>])", perl = TRUE))
unlist(strsplit("var>2", "(?=[=<>])", perl = TRUE))
Результаты:
[1] "var" "<" "3"
[1] "var" "=" "=" "5"
[1] "var" ">" "2"
Для второго примера выше я хотел бы получить [1] "var" "==" "5"
, поэтому два =
должны быть возвращены как один элемент. Как мне изменить свое регулярное выражение для достижения этого? (Я уже пробовал группировать и квантификаторы для "==", но ничего не получалось - регулярные выражения не мои друзья...)
Ответы
Ответ 1
Вы можете использовать регулярное выражение PCRE для соответствия требуемым подстрокам:
==|[<>]|(?:(?!==)[^<>])+
Чтобы также поддерживать !=
, измените его как
[!=]=|[<>]|(?:(?![=!]=)[^<>])+
Смотрите демонстрацию regex.
Подробнее
-
==
- 2 =
признаки
-
|
- или
-
[<>]
- a <
или >
-
|
- или
-
(?:(?!==)[^<>])+
- 1 или более символов, кроме <
и >
([^<>]
), которые не запускают последовательность ==
char (умеренный токен).
ПРИМЕЧАНИЕ. Это можно легко расширить, добавив больше альтернатив и отрегулировав темный жадный токен.
R test:
> text <- "Text1==text2<text3><More here"
> res <- regmatches(text, gregexpr("==|[<>]|(?:(?!==)[^<>])+", text, perl=TRUE))
> res
[[1]]
[1] "Text1" "==" "text2" "<" "text3" ">"
[7] "<" "More here"
Ответ 2
Расширяясь от моей идеи в комментариях, просто для форматирования:
tests=c("var==5","var<3","var.name>5")
regmatches(tests,regexec("([a-zA-Z0-9_.]+)(\\W+)([a-zA-Z0-9_.]+)",tests))
\w
[a-zA-Z0-9_]
и \w
напротив [^a-zA-Z0-9_]
, я расширил его после комментария для включения. в классе символов, а так как R не поддерживает \w в классе символов в базовом регулярном выражении (нужно использовать perl = TRUE).
Таким образом, регулярное выражение ищет не менее 1 из \w и., то минимум 1 не в \w (для сопоставления операторов), а затем не менее 1 из \w и точки.
Каждый шаг захватывается, и это дает:
[[1]]
[1] "var==5" "var" "==" "5"
[[2]]
[1] "var<3" "var" "<" "3"
[[3]]
[1] "var.name>5" "var.name" ">" "5"
вы можете добавить *
между каждой группой захвата, если ваши записи могут иметь место вокруг оператора, если не захват оператора не получит их.
Ответ 3
Использование границ слов (\\b
) и определение двух возможностей для поиска:
unlist(strsplit("var==5", "(?=(\\b[^a-zA-Z0-9])|(\\b[a-zA-Z0-9]\\b))", perl = TRUE))
[1] "var" "==" "5"
unlist(strsplit("var<3", "(?=(\\b[^a-zA-Z0-9])|(\\b[a-zA-Z0-9]\\b))", perl = TRUE))
[1] "var" "<" "3"
unlist(strsplit("var>2", "(?=(\\b[^a-zA-Z0-9])|(\\b[a-zA-Z0-9]\\b))", perl = TRUE))
[1] "var" ">" "2"
Пояснение:
Разделите в конце "слова", и после него есть либо буквенно-цифровой символ \\b[^a-zA-Z0-9]
, либо это конец "слова", а после него есть буквенно-цифровой символ.
EDIT:
На самом деле приведенный выше код имел бы неожиданные результаты, если число в конце равно 10 или более.
Другим вариантом является использование lookbehind
и разделение, если перед ним есть либо символ не алфавита, за которым следует ребро слова, либо символ алфавита, за которым следует край слова:
strsplit("var<20", "(?<=(([^a-zA-Z0-9]\\b)|([a-zA-Z0-9]\\b)))", perl = TRUE)[[1]]
#[1] "var" "<" "20"
strsplit("var==20", "(?<=(([^a-zA-Z0-9]\\b)|([a-zA-Z0-9]\\b)))", perl = TRUE)[[1]]
#[1] "var" "==" "20"
strsplit("var!=5", "(?<=(([^a-zA-Z0-9]\\b)|([a-zA-Z0-9]\\b)))", perl = TRUE)[[1]]
#[1] "var" "!=" "5"
EDIT2:
Полностью украсть метод @Tensibai для определения символов alphanum (+ underscore)/non alphanum, приведенный выше regex
можно упростить до: "(?<=((\\W\\b)|(\\w\\b)))"