Перевірте, чи є символи в рядку


279

Я намагаюся визначити, чи є рядок підмножиною іншої рядки. Наприклад:

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

Я хочу повернути TRUE, якщо "value" з'являється як частина рядка "chars". У наступному сценарії я хочу повернути помилкове:

chars <- "test"
value <- "et"

12
Прийнята відповідь неправильна, вам потрібно додати fixed=TRUE, інакше ви трактуєте її як регулярний вимір замість рядка. Дивіться мою відповідь з жовтня 2016 року
Джошуа Щока

@JoshuaCheek Якщо ви не маєте спеціальних символів у своєму шаблоні, регулярний вираз поверне той самий результат, що і зафіксований.
користувач3932000

1
Звичайно, але ви можете це знати лише якщо ви передаєте це буквально. Інакше ви не знатимете, які символи є у шаблоні, тому ви або використовуєте fixed=TRUEабо у вас є помилка, яка спокійно і тонко зіпсує ваші дані.
Джошуа Щока

Відповіді:


388

Використовуйте greplфункцію

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

Використовуйте, ?greplщоб дізнатися більше.


8
Для цього простого випадку додавання фіксованого = 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 (візерунок, х) принаймні у 2017 році
JMR

2
Це не повинно бути прийнятою відповіддю, оскільки значення буде інтерпретуватися як шаблон зворотного вираження. fix = ІСТИНА завжди повинна використовуватися, якщо ви не знаєте, що шуканий рядок не стане схожим на шаблон регулярного вираження. Відповідь Джошуа Кріка, наведена нижче, має дуже чітке пояснення цього і повинна бути прийнятою відповіддю.
бхалер

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 ЛОБАЛЬНИЕ R Регулярної E Xpression P ечаті», він буде читати рядки введення , а потім роздрукувати їх , якщо вони відповідали аргументам , які ви дали. "Глобальний" означав, що збіг може статися в будь-якому місці рядка введення, я поясню "Регулярне вираження" нижче, але ідея полягає в тому, що це розумніший спосіб зіставити рядок (R називає цього "символом", наприкладclass("abc") ) та "Друкувати" "тому що це програма командного рядка, випромінюючи висновок, це означає, що вона друкує у вихідний рядок.

Тепер grepпрограма - це в основному фільтр, від рядків введення до ліній виводу. І здається, що Rgrep функція аналогічно займе масив входів. З причин, які мені абсолютно невідомі (я почав грати з R лише годину тому), він повертає вектор індексів, які відповідають, а не список збігів.

Але повернемось до вашого первинного питання, що ми насправді хочемо - це знати, чи знайшли ми голку в стозі сіна, що є справжньою / хибною цінністю. Вони, мабуть, вирішили назвати цю функцію grepl, як у "grep", але з " L ogical" зворотним значенням (вони називають істинні та хибні логічні значення, наприклад class(TRUE)).

Отже, тепер ми знаємо, звідки пішла назва і що вона повинна робити. Повернемося до регулярних виразів. Аргументи, хоча вони є рядками, вони використовуються для побудови регулярних виразів (відтепер: регулярний вираз). Регекс - це спосіб зіставити рядок (якщо це визначення дратує вас, відпустіть його). Наприклад, регулярний вираз 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
"фіксований" відноситься до символів, що відповідають "фіксованій" послідовності.
Буде Беасон

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

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

20

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

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

Ти отримаєш

[1]  TRUE FALSE FALSE  TRUE

оскільки 1-е слово має підрядку "as", а останнє слово містить підрядку "at"


АБО оператор був саме тим, що мені потрібно! +1
Сем

10

Використовуйте grepабо , grepl але бути в курсі того, чи хочете ви використовувати регулярні вирази .

За замовчуванням grepі пов’язані з ними беруть відповідний регулярний вираз , а не буквальну підрядку. Якщо ви цього не очікуєте і намагаєтеся відповідати недійсному регулярному вираженню, це не спрацює:

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

Щоб зробити справжній тест підрядки, використовуйте fixed = TRUE.

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

Якщо ви хочете регулярно виразити, чудово, але це, як видається, не вимагає ОП.


7

Можна використовувати grep

grep("es", "Test")
[1] 1
grep("et", "Test")
integer(0)

0

Аналогічна проблема тут: Дано рядок і список ключових слів, виявіть, які, якщо такі є, ключові слова містяться в рядку.

Рекомендації з цієї теми пропонують stringr« 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щоб визначити, чи є якісь ключові слова в рядку.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.