Teste se os caracteres estão em uma sequência

279

Estou tentando determinar se uma string é um subconjunto de outra string. Por exemplo:

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

Quero retornar TRUE se "value" aparecer como parte da string "chars". No cenário a seguir, eu gostaria de retornar false:

chars <- "test"
value <- "et"
Mike
fonte
12
A resposta aceita está errada, você precisa adicionar fixed=TRUE, caso contrário, você a está tratando como uma expressão regular em vez de uma string. Veja minha resposta de outubro de 2016.
Joshua Cheek
@JoshuaCheek A menos que você tenha caracteres especiais em seu padrão, o regex retornará o mesmo resultado que o fixo.
user3932000
1
Claro, mas você só pode saber isso se estiver passando um literal. Caso contrário, você não saberá quais caracteres estão no padrão; portanto, você usa fixed=TRUEou possui um erro que atrapalha discretamente e sutilmente seus dados.
Joshua Cheek

Respostas:

388

Use a greplfunção

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

Use ?greplpara descobrir mais.

smu
fonte
8
Nesse caso simples, adicionar Fixed = TRUE pode melhorar o desempenho (supondo que você esteja executando muitas dessas computações).
Greg Neve
1
@ Josh O'brien, que pós constatação de comparação (contagem) todos os jogos em uma única cadeia longa, tente encontrar um jogo em um monte de cordas mais curtas: vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Neve
2
@GregSnow - Tentei system.time(a <- grepl("abc", vec))e system.time(a <- grepl("abc", vec, fixed=TRUE)), fixed=TRUEainda assim, se algo ligeiramente mais lento. A diferença não é apreciável com essas seqüências curtas, mas fixed=TRUEainda não parece ser mais rápida. Obrigado por apontar, porém, que é em longas seqüências que fixed=TRUElevam o verdadeiro sucesso.
Josh O'Brien
2
grepl (pattern, x) pelo menos em 2017
JMR
2
Essa não deve ser a resposta aceita, porque o valor será interpretado como um padrão de expressão regular. Fixed = TRUE sempre deve ser usado, a menos que você saiba que a string que você está procurando não parecerá com um padrão regex. A resposta de Joshua Creek abaixo tem uma explicação muito clara disso e deve ser a resposta aceita.
precisa saber é
159

Responda

Suspiro, levei 45 minutos para encontrar a resposta para esta pergunta simples. A resposta é: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

Interpretação

grepé nomeado após o executável do Linux, o que em si é um acrônimo de " G lobal R egular E xpression P rint", ele iria ler linhas de entrada e, em seguida, imprimi-los se eles combinavam com os argumentos que você deu. "Global" significou que a correspondência poderia ocorrer em qualquer lugar da linha de entrada, explicarei abaixo "Expressão regular" abaixo, mas a ideia é que é uma maneira mais inteligente de corresponder à string (R chama esse "caractere", por exemplo class("abc")) e "Print "porque é um programa de linha de comando, emitir saída significa que imprime na sua cadeia de saída.

Agora, o grepprograma é basicamente um filtro, das linhas de entrada às linhas de saída. E parece que a grepfunção de R, da mesma forma, terá uma matriz de entradas. Por razões totalmente desconhecidas para mim (eu só comecei a jogar com o R há cerca de uma hora), ele retorna um vetor dos índices correspondentes, em vez de uma lista de correspondências.

Mas, voltando à sua pergunta original, o que realmente queremos é saber se encontramos a agulha no palheiro, um valor verdadeiro / falso. Eles aparentemente decidiu nomear esta função grepl, como em "grep", mas com um " L valor de retorno ogical" (que eles chamam de valores lógicos verdadeiro e falso, por exemplo class(TRUE)).

Então, agora sabemos de onde o nome veio e o que ele deve fazer. Vamos voltar para expressões regulares. Os argumentos, mesmo que sejam cadeias, são usados ​​para criar expressões regulares (doravante: regex). Um regex é uma maneira de corresponder a uma string (se essa definição o irritar, deixe-o ir). Por exemplo, a regex acorresponde ao caractere "a", a regex a*corresponde ao caractere "a"0 ou mais vezes e a regex a+corresponde ao caractere "a"1 ou mais vezes. Portanto, no exemplo acima, a agulha que estamos procurando 1+2, quando tratada como regex, significa "um ou mais 1 seguido de um 2" ... mas o nosso é seguido de um sinal de mais!

1 + 2 como uma expressão regular

Portanto, se você usasse a greplconfiguração sem fixed, suas agulhas seriam acidentalmente palheiros e isso acidentalmente funcionaria com bastante frequência, podemos ver que funciona até para o exemplo do OP. Mas isso é um bug latente! Precisamos dizer que a entrada é uma string, não uma regex, que aparentemente fixedé para isso. Por que consertado? Nenhuma pista, marque esta resposta com um b / c. Você provavelmente terá que procurar mais 5 vezes antes de memorizá-la.

Algumas considerações finais

Quanto melhor o seu código, menos histórico você precisa conhecer para entender o código. Todo argumento pode ter pelo menos dois valores interessantes (caso contrário, não precisaria ser um argumento), os documentos listam 9 argumentos aqui, o que significa que há pelo menos 2 ^ 9 = 512 maneiras de invocá-lo, é muito trabalho para escreva, teste e lembre-se ... dissocie essas funções (divida-as, remova dependências umas das outras, as coisas de string são diferentes do que as expressões regulares, as coisas do vetor). Algumas das opções também são mutuamente exclusivas, não fornecem aos usuários maneiras incorretas de usar o código, ou seja, a chamada problemática deve ser estruturalmente sem sentido (como passar uma opção que não existe), não logicamente sem sentido (onde você precisa emitir um aviso para explicá-lo). Coloque metaforicamente: substituir a porta da frente no lado do 10º andar por uma parede é melhor do que pendurar uma placa que adverte contra seu uso, mas também é melhor que nenhuma. Em uma interface, a função define como devem ser os argumentos, e não o chamador (porque o chamador depende da função, inferindo tudo o que todos desejam chamar) faz com que a função dependa também dos chamadores e desse tipo de dependência cíclica entupirá rapidamente um sistema e nunca fornecerá os benefícios que você espera). Desconfie de tipos equivocados, é uma falha de design que coisas como inferir tudo o que todos podem querer chamar também faz com que a função dependa dos chamadores, e esse tipo de dependência cíclica entupirá rapidamente um sistema e nunca fornecerá os benefícios que você espera). Desconfie de tipos equivocados, é uma falha de design que coisas como inferir tudo o que todos podem querer chamar também faz com que a função dependa dos chamadores, e esse tipo de dependência cíclica entupirá rapidamente um sistema e nunca fornecerá os benefícios que você espera). Desconfie de tipos equivocados, é uma falha de design que coisas comoTRUEe 0e "abc"são todos vetores.

Joshua Cheek
fonte
6
Felicidades pela sua explicação! Parece que R evoluiu por um longo período de tempo e está preso a algumas opções de design estranhas (consulte, por exemplo, respostas a esta pergunta sobre tipos de valor ). No entanto, o retorno de um vetor de índices de correspondência parece apropriado nesse caso, assim como a grepfiltragem de linhas, não de células.
precisa saber é o seguinte
4
"fixo" refere-se aos caracteres que correspondem a uma sequência "fixa".
Will Beason
32

Você quer grepl:

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

Use esta função do stringipacote:

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

Alguns benchmarks:

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
fonte
22

Além disso, pode ser feito usando a biblioteca "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
fonte
20

Caso você também queira verificar se uma string (ou um conjunto de strings) contém várias sub-strings, você também pode usar o '|' entre duas substrings.

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

Você vai ter

[1]  TRUE FALSE FALSE  TRUE

já que a 1ª palavra tem substring "as" e a última palavra contém substring "at"

C. Zeng
fonte
O operador OR era exatamente o que eu precisava! +1
Sam
10

Use grepou grepl esteja ciente de que deseja ou não usar expressões regulares .

Por padrão, grepe relacionados usam uma expressão regular para corresponder, não uma substring literal. Se você não espera isso e tenta corresponder a um regex inválido, ele não funciona:

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

Para fazer um verdadeiro teste de substring, use fixed = TRUE.

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

Se você quer regex, ótimo, mas não é isso que o OP parece estar perguntando.

Chris
fonte
7

Você pode usar grep

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

Problema semelhante aqui: dada uma string e uma lista de palavras-chave, detecte quais delas, se houver, estão contidas na string.

Recomendações de esta discussão sugerem stringr's str_detecte grepl. Aqui estão os benchmarks do microbenchmarkpacote:

Usando

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

e depois

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

nós achamos

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

Como você pode ver, mais de 5.000 iterações da pesquisa de palavras-chave usando str_detecte greplsobre uma sequência prática e um vetor de palavras-chave têm um grepldesempenho um pouco melhor que str_detect.

O resultado é o vetor booleano rque identifica quais, se houver alguma, das palavras-chave estão contidas na string.

Portanto, recomendo usar greplpara determinar se alguma palavra-chave está em uma sequência.

Alex L
fonte