web-dev-qa-db-fra.com

Tester si les caractères sont dans une chaîne

J'essaie de déterminer si une chaîne est un sous-ensemble d'une autre chaîne. Par exemple:

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

Je veux renvoyer VRAI si "valeur" apparaît dans la chaîne "caractères". Dans le scénario suivant, je voudrais renvoyer false:

chars <- "test"
value <- "et"
235
mike

Utilisez la fonction grepl

grepl(value, chars)
# TRUE
332
smu

Répondre

Soupir, il m'a fallu 45 minutes pour trouver la réponse à cette question simple. La réponse est: 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

Interprétation

grep est nommé d'après l'exécutable de Linux, qui est lui-même un acronyme de "G lobal R egulaire E xpression P rint ", il lirait les lignes d’entrée puis les imprimerait si elles correspondaient aux arguments que vous avez donnés. "Global" signifiait que la correspondance pouvait avoir lieu n'importe où sur la ligne d'entrée. Je vais expliquer "Expression régulière" ci-dessous, mais l'idée est que c'est un moyen plus intelligent de faire correspondre la chaîne (R appelle ce "caractère", par exemple, class("abc") ), et "Print" parce qu’il s’agit d’un programme en ligne de commande, émettre une sortie signifie qu’il s’imprime dans sa chaîne de sortie.

Maintenant, le programme grep est essentiellement un filtre, des lignes d’entrée aux lignes de sortie. Et il semble que la fonction grep de R nécessitera un tableau d'entrées. Pour des raisons qui me sont totalement inconnues (j'ai commencé à jouer avec R il y a à peu près une heure), il renvoie un vecteur des index correspondants, plutôt qu'une liste de correspondances.

Mais revenons à votre question initiale. Ce que nous voulons vraiment, c’est de savoir si nous avons trouvé l’aiguille dans la botte de foin, une valeur vrai/faux. Ils ont apparemment décidé de nommer cette fonction grepl, comme dans "grep" mais avec une valeur de retour "L ogical" (ils appellent valeurs logiques vraies et fausses, par exemple class(TRUE)).

Nous savons donc maintenant d'où vient le nom et ce qu'il est censé faire. Permet de revenir aux expressions régulières. Les arguments, même s’il s’agit de chaînes, sont utilisés pour construire des expressions régulières (dorénavant: regex). Une expression rationnelle est un moyen de faire correspondre une chaîne (si cette définition vous irrite, laissez-la aller). Par exemple, la regex a correspond au caractère "a", la regex a* correspond au caractère "a" 0 fois ou plus, et la regex a+ correspond au caractère "a" 1 fois ou plus. Par conséquent, dans l'exemple ci-dessus, l'aiguille que nous recherchons 1+2, lorsqu'elle est traitée comme une regex, signifie "un ou plusieurs 1 suivis d'un 2" ... mais la nôtre est suivie d'un plus!

1+2 as a regex

Donc, si vous utilisiez la grepl sans régler fixed, vos aiguilles seraient accidentellement des meules de foin, et cela fonctionnerait accidentellement assez souvent, nous pouvons même constater que cela fonctionne également à l'exemple du PO. Mais c'est un bug latent! Nous devons lui dire que l’entrée est une chaîne, pas une regex, ce qui est apparemment ce à quoi sert fixed. Pourquoi fixe? Pas la moindre idée, marquez cette réponse b/c, vous allez probablement devoir la regarder 5 fois de plus avant de la mémoriser.

Quelques dernières réflexions

Plus votre code est bon, moins vous devez connaître l’histoire pour en comprendre le sens. Chaque argument peut avoir au moins deux valeurs intéressantes (sinon, ce ne serait pas nécessairement un argument), la documentation répertorie 9 arguments ici, ce qui signifie qu'il y a au moins 2 ^ 9 = 512 façons de l'invoquer, ce qui représente beaucoup de travail pour écrivez, testez et souvenez-vous ... dissociez ces fonctions (séparez-les, supprimez les dépendances les unes par rapport aux autres, les chaînes de caractères sont différentes des choses regex, elles sont différentes de celles des vecteurs). Certaines options s’excluent également mutuellement. Ne donnez pas aux utilisateurs des moyens incorrects d’utiliser le code, c’est-à-dire que l’appel problématique doit être structurellement dépourvu de sens (par exemple, passer une option qui n’existe pas), pas logiquement dénué de sens (vous devez émettre un avertissement pour l'expliquer). Métaphoriquement: remplacer la porte d'entrée située sur le côté du 10ème étage par un mur est mieux que de suspendre un panneau qui met en garde contre son utilisation, mais que cela vaut mieux que ni l'un ni l'autre. Dans une interface, la fonction définit l'apparence des arguments, et non l'appelant (l'appelant dépend de la fonction, en déduisant tout ce que tout le monde peut appeler, la fonction dépend également des appelants, et ce type de dépendance cyclique va rapidement encombrer un système et ne jamais fournir les avantages escomptés). Méfiez-vous des types équivoques, c’est un défaut de conception que des éléments comme TRUE et 0 et "abc" soient tous des vecteurs.

132
Joshua Cheek

Vous voulez grepl:

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

Utilisez cette fonction du package stringi:

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

Quelques repères:

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
bartektartanus

Aussi, peut être fait en utilisant la bibliothèque "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
16
Surya

Juste au cas où vous voudriez aussi vérifier si une chaîne (ou un ensemble de chaînes) contient plusieurs sous-chaînes, vous pouvez également utiliser le caractère '|' entre deux sous-chaînes.

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

Tu auras

[1]  TRUE FALSE FALSE  TRUE

puisque le premier mot a une sous-chaîne "en", et le dernier mot contient une sous-chaîne "à"

15
C. Zeng

Utilisez grep ou greplmais sachez si vous voulez ou non utiliser des expressions régulières.

Par défaut, grep et les entités associées utilisent une expression régulière , et non une sous-chaîne littérale. Si vous ne vous attendez pas à cela et que vous essayez de faire correspondre une expression rationnelle invalide, cela ne fonctionne pas:

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

Pour faire un vrai test de sous-chaîne, utilisez fixed = TRUE.

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

Si vous voulez regex, c'est bien, mais ce n'est pas ce que le PO semble demander.

9
Chris

Vous pouvez utiliser grep

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