Проверьте, находятся ли символы в строке

279

Я пытаюсь определить, является ли строка подмножеством другой строки. Например:

chars <- "test"
value <- "es"

Я хочу вернуть TRUE, если «value» появляется как часть строки «chars». В следующем сценарии я хотел бы вернуть false:

chars <- "test"
value <- "et"
Майк
источник
12
Принятый ответ неверен, вам нужно добавить fixed=TRUE, иначе вы воспринимаете его как регулярное выражение вместо строки. Смотрите мой ответ с октября 2016 года.
Джошуа Чик
@JoshuaCheek Если в вашем шаблоне нет специальных символов, регулярное выражение вернет тот же результат, что и фиксированный.
user3932000
1
Конечно, но вы можете знать это только если вы передаете это буквально. В противном случае, вы не будете знать, какие символы в шаблоне, поэтому вы либо используете, fixed=TRUEлибо у вас есть ошибка, которая тихо и незаметно испортит ваши данные.
Джошуа Чик

Ответы:

388

Используйте greplфункцию

grepl(value, chars, fixed = TRUE)
# TRUE

Используйте, ?greplчтобы узнать больше.

СМУ
источник
8
В этом простом случае добавление fixed = TRUE может повысить производительность (при условии, что вы будете выполнять много этих вычислений).
Грег Сноу
1
@Josh О'Брайны, что пост по сравнению вывод (подсчет) все матчи в одной длинной строке, попытайтесь найти 1 матч в куче коротких строк: vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Грег Сноу
2
@GregSnow - Пробовал system.time(a <- grepl("abc", vec))и system.time(a <- grepl("abc", vec, fixed=TRUE)), и fixed=TRUEдо сих пор, если что - нибудь немного медленнее. Разница невелика с этими короткими строками, но fixed=TRUE, похоже, не быстрее. Спасибо, что указали, однако, что это на длинных струнах, которые fixed=TRUEпринимают настоящий удар.
Джош О'Брайен
2
grepl (pattern, x) по крайней мере в 2017 году
JMR
2
Это не должен быть принятый ответ, потому что значение будет интерпретироваться как шаблон регулярного выражения. fixed = TRUE следует всегда использовать, если только вы не знаете, что искомая строка не будет выглядеть как шаблон регулярного выражения. Ответ Джошуа Крик ниже имеет очень четкое объяснение этого, и должен быть принятым ответом.
bhaller
159

Ответ

Вздох, мне понадобилось 45 минут, чтобы найти ответ на этот простой вопрос. Ответ:grepl(needle, haystack, fixed=TRUE)

# Correct
> grepl("1+2", "1+2", fixed=TRUE)
[1] TRUE
> grepl("1+2", "123+456", fixed=TRUE)
[1] FALSE

# Incorrect
> grepl("1+2", "1+2")
[1] FALSE
> grepl("1+2", "123+456")
[1] TRUE

интерпретация

grepназван в честь исполняемого файла linux, который сам по себе является аббревиатурой « G lobal R egular E xpression P rint», он будет читать строки ввода и затем печатать их, если они соответствуют аргументам, которые вы дали. «Глобальный» означал, что совпадение может произойти в любом месте строки ввода, я объясню «Регулярное выражение» ниже, но идея в том, что это более разумный способ сопоставления строки (например, R вызывает этот «символ» class("abc")) и «Печать». msgstr "потому что это программа командной строки, испускание вывода означает, что он печатает в свою строку вывода

Теперь grepпрограмма представляет собой фильтр, от строк ввода до строк вывода. И похоже, что grepфункция R аналогично будет принимать массив входов. По причинам, которые мне совершенно неизвестны (я начал играть с R около часа назад), он возвращает вектор совпадающих индексов, а не список совпадений.

Но, возвращаясь к вашему первоначальному вопросу, мы действительно хотим узнать, нашли ли мы иголку в стоге сена, истинное / ложное значение. Они , видимо , решили назвать эту функцию grepl, как и в «Grep» , но с « L ogical» возвращаемого значения (они называют истинные и ложные логические значения, например class(TRUE)).

Итак, теперь мы знаем, откуда пришло название и что оно должно делать. Вернемся к регулярным выражениям. Аргументы, даже если они являются строками, они используются для построения регулярных выражений (далее: regex). Регулярное выражение - это способ сопоставления строки (если это определение вас раздражает, отпустите его). Например, регулярное выражение aсоответствует символу "a", регулярное выражение a*соответствует символу "a"0 или более раз, а регулярное выражение a+соответствует символу "a"1 или более раз. Следовательно, в приведенном выше примере стрелка, которую мы ищем 1+2, когда рассматривается как регулярное выражение, означает «один или более 1, а затем 2» ... но за нашей следует плюс!

1 + 2 в качестве регулярного выражения

Таким образом, если вы используете greplбез установки fixed, ваши иглы будут случайно стогами сена, и это будет работать случайно довольно часто, мы можем видеть, что это работает даже на примере ОП. Но это скрытая ошибка! Нам нужно сказать, что входные данные - это строка, а не регулярное выражение, что, по-видимому, fixedи для этого. Почему исправлено? Понятия не имею, добавьте этот ответ в закладки, потому что вам, вероятно, придется искать его еще 5 раз, прежде чем вы его запомните.

Несколько заключительных мыслей

Чем лучше ваш код, тем меньше истории вы должны знать, чтобы понять его. Каждый аргумент может иметь по крайней мере два интересных значения (в противном случае он не должен быть аргументом), в документе приведен список из 9 аргументов, что означает, что есть как минимум 2 ^ 9 = 512 способов вызвать его, это много работы для писать, тестировать и запоминать ... разъединять такие функции (разбивать их, удалять зависимости друг от друга, строковые объекты отличаются от регулярных выражений, а не от векторных). Некоторые из этих опций также являются взаимоисключающими, не дают пользователям неправильных способов использования кода, то есть проблемный вызов должен быть структурно бессмысленным (например, передавать несуществующую опцию), а не логически бессмысленным (где вы должны выдать предупреждение, чтобы объяснить это). Положите в переносном смысле: заменить входную дверь на 10-м этаже стеной лучше, чем вывесить табличку, предупреждающую о ее использовании, но лучше либо нет, либо нет. В интерфейсе функция определяет, как должны выглядеть аргументы, а не вызывающая сторона (поскольку вызывающая сторона зависит от функции, вывод всего того, с чем каждый может захотеть вызвать ее, делает функцию зависимой и от вызывающих, и от этого типа циклическая зависимость быстро забьет систему и никогда не даст ожидаемых результатов). Будьте очень осторожны с двусмысленными типами, это недостаток дизайна, который Вывод всего, что каждый может захотеть вызвать, делает функцию зависимой и от вызывающих, и этот тип циклической зависимости быстро забьет систему и никогда не даст ожидаемых результатов). Будьте очень осторожны с двусмысленными типами, это недостаток дизайна, который Вывод всего, что каждый может захотеть вызвать, делает функцию зависимой и от вызывающих, и этот тип циклической зависимости быстро забьет систему и никогда не даст ожидаемых результатов). Будьте очень осторожны с двусмысленными типами, это недостаток дизайна, которыйTRUEи 0и "abc"все векторы.

Джошуа Чик
источник
6
Приветствия за ваше объяснение! Кажется, что R эволюционировал в течение длительного периода времени и застрял с некоторыми странными проектными решениями (см., Например, ответы на этот вопрос о типах значений ). Однако в этом случае представляется целесообразным возвращать вектор индексов совпадения, а также grepфильтровать строки, а не ячейки.
krevelen
4
«фиксированный» относится к символам, соответствующим «фиксированной» последовательности.
Will
32

Вы хотите grepl:

> chars <- "test"
> value <- "es"
> grepl(value, chars)
[1] TRUE
> chars <- "test"
> value <- "et"
> grepl(value, chars)
[1] FALSE
Джастин
источник
27

Используйте эту функцию из stringiпакета:

> stri_detect_fixed("test",c("et","es"))
[1] FALSE  TRUE

Некоторые тесты:

library(stringi)
set.seed(123L)
value <- stri_rand_strings(10000, ceiling(runif(10000, 1, 100))) # 10000 random ASCII strings
head(value)

chars <- "es"
library(microbenchmark)
microbenchmark(
   grepl(chars, value),
   grepl(chars, value, fixed=TRUE),
   grepl(chars, value, perl=TRUE),
   stri_detect_fixed(value, chars),
   stri_detect_regex(value, chars)
)
## Unit: milliseconds
##                               expr       min        lq    median        uq       max neval
##                grepl(chars, value) 13.682876 13.943184 14.057991 14.295423 15.443530   100
##  grepl(chars, value, fixed = TRUE)  5.071617  5.110779  5.281498  5.523421 45.243791   100
##   grepl(chars, value, perl = TRUE)  1.835558  1.873280  1.956974  2.259203  3.506741   100
##    stri_detect_fixed(value, chars)  1.191403  1.233287  1.309720  1.510677  2.821284   100
##    stri_detect_regex(value, chars)  6.043537  6.154198  6.273506  6.447714  7.884380   100
bartektartanus
источник
22

Также это можно сделать с помощью библиотеки "stringr":

> library(stringr)
> chars <- "test"
> value <- "es"
> str_detect(chars, value)
[1] TRUE

### For multiple value case:
> value <- c("es", "l", "est", "a", "test")
> str_detect(chars, value)
[1]  TRUE FALSE  TRUE FALSE  TRUE
Surya
источник
20

На всякий случай, если вы также хотите проверить, содержит ли строка (или набор строк) несколько подстрок, вы также можете использовать '|' между двумя подстроками.

>substring="as|at"
>string_vector=c("ass","ear","eye","heat") 
>grepl(substring,string_vector)

Ты получишь

[1]  TRUE FALSE FALSE  TRUE

так как 1-е слово имеет подстроку «как», а последнее слово содержит подстроку «в»

К. Цзэн
источник
Оператор OR был именно то, что мне нужно! +1
Сэм
10

Используйте grepили grepl но знайте, хотите ли вы использовать регулярные выражения .

По умолчанию grepи related принимают регулярное выражение для сопоставления, а не литеральную подстроку. Если вы этого не ожидаете и пытаетесь найти недопустимое регулярное выражение, это не сработает:

> grep("[", "abc[")
Error in grep("[", "abc[") : 
  invalid regular expression '[', reason 'Missing ']''

Чтобы сделать настоящий тест подстроки, используйте fixed = TRUE.

> grep("[", "abc[", fixed = TRUE)
[1] 1

Если вы хотите регулярное выражение, отлично, но это не то, о чем спрашивает OP.

Крис
источник
7

Ты можешь использовать grep

grep("es", "Test")
[1] 1
grep("et", "Test")
integer(0)
Nico
источник
0

Аналогичная проблема здесь: учитывая строку и список ключевых слов, определите, какие из ключевых слов, если таковые имеются, содержатся в строке.

Рекомендации этой темы предлагают stringr«s str_detectи grepl. Вот тесты из microbenchmarkпакета:

С помощью

map_keywords = c("once", "twice", "few")
t = "yes but only a few times"

mapper1 <- function (x) {
  r = str_detect(x, map_keywords)
}

mapper2 <- function (x) {
  r = sapply(map_keywords, function (k) grepl(k, x, fixed = T))
}

а потом

microbenchmark(mapper1(t), mapper2(t), times = 5000)

мы нашли

Unit: microseconds
       expr    min     lq     mean  median      uq      max neval
 mapper1(t) 26.401 27.988 31.32951 28.8430 29.5225 2091.476  5000
 mapper2(t) 19.289 20.767 24.94484 23.7725 24.6220 1011.837  5000

Как вы можете видеть, более 5000 итераций поиск по ключевым словам с помощью str_detectи greplпо практической строки и вектора ключевых слов, greplвыполняет совсем немного лучше str_detect.

Результатом является логический вектор, rкоторый определяет, какие из ключевых слов, если таковые имеются, содержатся в строке.

Поэтому я рекомендую использовать, greplчтобы определить, есть ли какие-либо ключевые слова в строке.

Алекс Л
источник