Sprawdź, czy znaki są w ciągu


279

Próbuję ustalić, czy ciąg znaków jest podzbiorem innego ciągu. Na przykład:

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

Chcę zwrócić PRAWDA, jeśli „wartość” pojawia się jako część ciągu „chars”. W poniższym scenariuszu chciałbym zwrócić wartość false:

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

12
Przyjęta odpowiedź jest błędna, musisz ją dodać fixed=TRUE, w przeciwnym razie traktujesz ją jako wyrażenie regularne zamiast ciągu. Zobacz moją odpowiedź z października 2016 r.
Joshua Cheek

@JoshuaCheek Jeśli nie masz znaków specjalnych we wzorcu, wyrażenie regularne zwróci ten sam wynik, co ustalony.
user3932000,

1
Jasne, ale możesz to wiedzieć tylko wtedy, gdy przekazujesz to dosłownie. W przeciwnym razie nie będziesz wiedział, jakie znaki są we wzorze, więc albo używasz, fixed=TRUEalbo masz błąd, który po cichu i subtelnie zepsuje twoje dane.
Joshua Cheek

Odpowiedzi:


388

Użyj greplfunkcji

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

Użyj, ?greplaby dowiedzieć się więcej.


8
W tym prostym przypadku dodanie fixed = TRUE może poprawić wydajność (przy założeniu, że będziesz wykonywać wiele takich obliczeń).
Greg Snow,

1
@Josh O'Brien, że po porównaniu odkrycie (liczenie) wszystkie mecze w jednym długim ciągiem, spróbuj znaleźć 1 wynik w pęczek krótsze ciągi: vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Snow,

2
@GregSnow - Wypróbowane system.time(a <- grepl("abc", vec))i system.time(a <- grepl("abc", vec, fixed=TRUE)), i fixed=TRUEnadal jest, jeśli w ogóle nieco wolniej. Różnica nie jest znacząca w przypadku tych krótkich łańcuchów, ale fixed=TRUEnadal nie wydaje się być szybsza. Dzięki za zwrócenie uwagi, że to na długich strunach fixed=TRUEzabiera prawdziwy hit.
Josh O'Brien

2
grepl (wzór, x) co najmniej w 2017 r.
JMR

2
To nie powinna być zaakceptowana odpowiedź, ponieważ wartość będzie interpretowana jako wzorzec wyrażenia regularnego. Naprawiono = PRAWDA powinna być zawsze używana, chyba że wiesz, że szukany ciąg nie będzie wyglądał jak wzorzec regularny. Poniższa odpowiedź Joshua Creek ma bardzo jasne wytłumaczenie i powinna być zaakceptowaną odpowiedzią.
bhaller

159

Odpowiedź

Westchnienie, znalezienie odpowiedzi na to proste pytanie zajęło mi 45 minut. Odpowiedź to: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

Interpretacja

grepnosi nazwę pliku wykonywalnego linux, który sam jest skrótem od „ G skroniowe R egular E Xpression P rukuj”, to czytać wiersze wejścia, a następnie wydrukować je, jeśli one dopasowane argumenty, jakie zostały podane. „Globalny” oznacza, że ​​dopasowanie może wystąpić w dowolnym miejscu w wierszu wprowadzania, wyjaśnię poniżej „Wyrażenie regularne”, ale pomysł polega na tym, że jest to mądrzejszy sposób dopasowania ciągu (np. R nazywa to „znakiem” class("abc")) i „Drukuj” „ponieważ jest to program wiersza poleceń, wysyłanie danych wyjściowych oznacza, że ​​wypisuje je na ciąg wyjściowy.

Teraz grepprogram jest w zasadzie filtrem, od linii wejściowych do linii wyjściowych. I wygląda na to, że grepfunkcja R podobnie pobierze szereg danych wejściowych. Z powodów, które są mi zupełnie nieznane (zacząłem grać z R około godzinę temu), zwraca wektor pasujących indeksów, a nie listę dopasowań.

Ale wracając do twojego pierwotnego pytania, tak naprawdę chcemy wiedzieć, czy znaleźliśmy igłę w stogu siana, wartość prawda / fałsz. Najwyraźniej postanowili nazwać tę funkcję grepl, jak w „grep”, ale z wartością zwrotną „ L ogical” (nazywają np. Prawdą i fałszem wartości logiczne class(TRUE)).

Teraz wiemy, skąd wzięła się nazwa i co ma robić. Wróćmy do wyrażeń regularnych. Argumenty, mimo że są łańcuchami, służą do budowania wyrażeń regularnych (odtąd: wyrażenie regularne). Wyrażenie regularne to sposób dopasowania łańcucha (jeśli ta definicja cię denerwuje, puść go). Na przykład regex apasuje do znaku "a", regex a*pasuje do znaku "a"0 lub więcej razy, a regex a+pasuje do znaku "a"1 lub więcej razy. Dlatego w powyższym przykładzie szukana przez nas igła 1+2, gdy jest traktowana jako wyrażenie regularne, oznacza „jeden lub więcej 1, a następnie 2” ... ale po naszym następuje plus!

1 + 2 jako wyrażenie regularne

Więc jeśli użyjesz greplustawienia bez fixed, twoje igły przypadkowo staną się stogami siana, a to przypadkowo zadziała dość często, widzimy, że działa nawet na przykład OP. Ale to ukryty błąd! Musimy powiedzieć, że wejście jest ciągiem, a nie wyrażeniem regularnym, co najwyraźniej fixedjest po to. Dlaczego naprawiony? Nie mam pojęcia, dodaj tę odpowiedź do zakładek b / c prawdopodobnie będziesz musiał sprawdzić jeszcze 5 razy, zanim ją zapamiętasz.

Kilka ostatnich myśli

Im lepszy kod, tym mniej historii musisz znać, aby go zrozumieć. Każdy argument może mieć co najmniej dwie interesujące wartości (w przeciwnym razie nie musiałby to być argument), w dokumencie znajduje się lista 9 argumentów, co oznacza, że ​​istnieją co najmniej 2 ^ 9 = 512 sposobów na wywołanie go, to dużo pracy, aby pisz, testuj i zapamiętaj ... rozłącz takie funkcje (rozdziel je, usuń zależności od siebie, łańcuch znaków jest inny niż łańcuch wyrażeń regularnych różni się od elementów wektorowych). Niektóre opcje wzajemnie się wykluczają, nie dają użytkownikom niewłaściwych sposobów korzystania z kodu, tzn. Problematyczne wywołanie powinno być strukturalnie nonsensowne (na przykład przekazywanie opcji, która nie istnieje), a nie logiczne nonsensowne (gdzie trzeba wyślij ostrzeżenie, aby to wyjaśnić). Mówiąc metaforycznie: zastąpienie drzwi wejściowych z boku 10. piętra ścianą jest lepsze niż zawieszenie znaku ostrzegającego przed jego użyciem, ale jedno z nich jest lepsze niż żadne. W interfejsie funkcja określa, jak powinny wyglądać argumenty, a nie osoba wywołująca (ponieważ funkcja wywołująca zależy od funkcji, co powoduje, że wszystko, z czym wszyscy mogą chcieć ją wywołać, powoduje, że funkcja zależy również od osób wywołujących i tego typu cyklicznej zależności szybko zatyka system i nigdy nie zapewnia oczekiwanych korzyści). Uważaj na niejednoznaczne typy, to wada projektowa, którą lubią wnioskowanie o wszystkim, z czym każdy może chcieć to nazwać, powoduje, że funkcja zależy również od dzwoniących, a tego rodzaju cykliczna zależność szybko zablokuje system i nigdy nie zapewni oczekiwanych korzyści). Uważaj na niejednoznaczne typy, to wada projektowa, którą lubią wnioskowanie o wszystkim, z czym każdy może chcieć to nazwać, powoduje, że funkcja zależy również od dzwoniących, a tego rodzaju cykliczna zależność szybko zablokuje system i nigdy nie zapewni oczekiwanych korzyści). Uważaj na niejednoznaczne typy, to wada projektowa, którą lubiąTRUEi 0a "abc"są wektory.


6
Pozdrawiam za wyjaśnienie! Wydaje się, że R ewoluował przez długi czas i utknął w dziwnych wyborach projektowych (patrz np. Odpowiedzi na to pytanie dotyczące typów wartości ). Jednak zwracanie wektora wskaźników dopasowania wydaje się w tym przypadku właściwe, podobnie jak grepfiltrowanie wierszy, a nie komórek.
krevelen 24.04.17

4
„ustalony” odnosi się do znaków pasujących do sekwencji „ustalonej”.
Will Beason,

32

Chcesz grepl:

> chars <- "test"
> value <- "es"
> grepl(value, chars)
[1] TRUE
> chars <- "test"
> value <- "et"
> grepl(value, chars)
[1] FALSE

27

Użyj tej funkcji z stringipakietu:

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

Niektóre punkty odniesienia:

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

Można to również zrobić za pomocą biblioteki „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

Na wypadek gdybyś chciał również sprawdzić, czy ciąg znaków (lub zestaw ciągów) zawiera wiele ciągów podrzędnych, możesz również użyć „|” między dwoma podciągami.

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

Dostaniesz

[1]  TRUE FALSE FALSE  TRUE

ponieważ pierwsze słowo ma podłańcuch „as”, a ostatnie słowo zawiera podłańcuch „at”


Operator OR był dokładnie tym, czego potrzebowałem! +1
Sam

10

Użyj greplub, grepl ale pamiętaj, czy chcesz używać wyrażeń regularnych .

Domyślnie grepi pokrewne biorą do dopasowania wyrażenie regularne , a nie dosłowne podłańcuch. Jeśli nie oczekujesz tego i próbujesz dopasować do niepoprawnego wyrażenia regularnego, to nie działa:

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

Aby wykonać prawdziwy test podciągów, użyj fixed = TRUE.

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

Jeśli chcesz regex, świetnie, ale nie o to pyta OP.


7

Możesz użyć grep

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

0

Podobny problem tutaj: Biorąc pod uwagę ciąg znaków i listę słów kluczowych, wykryj, które ze słów kluczowych są zawarte w ciągu.

Zalecenia tego wątku sugerują stringr„s str_detecti grepl. Oto punkty odniesienia z microbenchmarkpakietu:

Za pomocą

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))
}

i wtedy

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

znaleźliśmy

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

Jak widać, ponad 5000 iteracji wyszukiwania słów kluczowych za pomocą str_detecti greplponad praktycznym ciągiem znaków i wektorem słów kluczowych grepldziała znacznie lepiej niż str_detect.

Wynikiem jest wektor boolowski, rktóry identyfikuje, które ze słów kluczowych są zawarte w ciągu.

Dlatego zalecam użycie grepldo ustalenia, czy jakieś słowa kluczowe są w ciągu.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.