Разделить вектор символа на знаках сравнения 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)))"