Testen Sie, ob sich Zeichen in einer Zeichenfolge befinden

279

Ich versuche festzustellen, ob eine Zeichenfolge eine Teilmenge einer anderen Zeichenfolge ist. Beispielsweise:

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

Ich möchte TRUE zurückgeben, wenn "value" als Teil der Zeichenfolge "chars" angezeigt wird. Im folgenden Szenario möchte ich false zurückgeben:

chars <- "test"
value <- "et"
Mike
quelle
12
Die akzeptierte Antwort ist falsch, Sie müssen hinzufügen fixed=TRUE, andernfalls behandeln Sie sie als regulären Ausdruck anstelle einer Zeichenfolge. Siehe meine Antwort vom Oktober 2016.
Joshua Cheek
@JoshuaCheek Sofern Ihr Muster keine Sonderzeichen enthält, gibt Regex das gleiche Ergebnis wie fest zurück.
user3932000
1
Sicher, aber das kann man nur wissen, wenn man es wörtlich übergibt. Andernfalls wissen Sie nicht, welche Zeichen sich im Muster befinden, sodass Sie entweder verwenden fixed=TRUEoder einen Fehler haben, der Ihre Daten leise und subtil durcheinander bringt.
Joshua Cheek

Antworten:

387

Verwenden Sie die greplFunktion

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

Verwenden Sie ?grepl, um mehr zu erfahren.

smu
quelle
8
In diesem einfachen Fall kann das Hinzufügen von fixed = TRUE die Leistung verbessern (vorausgesetzt, Sie führen viele dieser Berechnungen durch).
Greg Snow
1
@Josh O'brien, in diesem Beitrag wurde das Finden (Zählen) aller Übereinstimmungen in einer einzelnen langen Zeichenfolge verglichen. Versuchen Sie, 1 Übereinstimmung in einer Reihe kürzerer Zeichenfolgen zu finden : vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Snow
2
@ GregSnow - Versucht system.time(a <- grepl("abc", vec))und system.time(a <- grepl("abc", vec, fixed=TRUE))und fixed=TRUEist immer noch etwas langsamer. Der Unterschied ist bei diesen kurzen Saiten nicht spürbar, fixed=TRUEscheint aber nicht schneller zu sein. Vielen Dank, dass Sie darauf hingewiesen haben, dass es sich um lange Saiten handelt, fixed=TRUEdie den wahren Erfolg bringen.
Josh O'Brien
2
grepl (Muster, x) mindestens im Jahr 2017
JMR
2
Dies sollte nicht die akzeptierte Antwort sein, da der Wert als Regex-Muster interpretiert wird. fixed = TRUE sollte immer verwendet werden, es sei denn, Sie wissen, dass die gesuchte Zeichenfolge nicht wie ein Regex-Muster aussieht. Die Antwort von Joshua Creek unten hat eine sehr klare Erklärung dafür und sollte die akzeptierte Antwort sein.
bhaller
159

Antworten

Seufz, ich habe 45 Minuten gebraucht, um die Antwort auf diese einfache Frage zu finden. Die Antwort ist: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

Deutung

grepnach dem Linux ausführbar benannt ist, die selbst eine Abkürzung von „ G lobale R egular E xpression P rint“, wäre es Eingabezeilen lesen und sie dann drucken , wenn sie die Argumente abgestimmt Sie gab. "Global" bedeutete, dass die Übereinstimmung an einer beliebigen Stelle in der Eingabezeile auftreten kann. Ich werde unten "Regulärer Ausdruck" erläutern, aber die Idee ist, dass es eine intelligentere Methode ist, die Zeichenfolge (R nennt dieses "Zeichen", z. B. class("abc")) und "Drucken" "Da es sich um ein Befehlszeilenprogramm handelt, bedeutet das Ausgeben von Ausgaben, dass es in seine Ausgabezeichenfolge gedruckt wird.

Jetzt ist das grepProgramm im Grunde ein Filter, von Eingabezeilen bis zu Ausgabezeilen. Und es scheint, dass die grepFunktion von R in ähnlicher Weise eine Reihe von Eingaben benötigt. Aus Gründen, die mir völlig unbekannt sind (ich habe erst vor ungefähr einer Stunde angefangen, mit R zu spielen), wird ein Vektor der übereinstimmenden Indizes anstelle einer Liste von Übereinstimmungen zurückgegeben.

Zurück zu Ihrer ursprünglichen Frage: Wir möchten wirklich wissen, ob wir die Nadel im Heuhaufen gefunden haben, einen wahren / falschen Wert. Sie entschieden sie offenbar um diese Funktion zu nennen grepl, wie sie in „grep“ aber mit einem „ L großartigen Puzzle“ Rückgabewert (sie nennen wahr und falsch logische Werte, zum Beispiel class(TRUE)).

Jetzt wissen wir also, woher der Name kommt und was er tun soll. Kehren wir zu den regulären Ausdrücken zurück. Die Argumente werden, obwohl sie Zeichenfolgen sind, verwendet, um reguläre Ausdrücke zu erstellen (fortan: Regex). Eine Regex ist eine Möglichkeit, eine Zeichenfolge abzugleichen (wenn Sie diese Definition irritiert, lassen Sie sie los). Zum Beispiel astimmt der "a"reguläre Ausdruck mit dem Zeichen überein , der reguläre Ausdruck a*stimmt mindestens "a"0 Mal a+mit dem Zeichen überein und der reguläre Ausdruck würde mindestens "a"1 Mal mit dem Zeichen übereinstimmen . Daher bedeutet im obigen Beispiel die Nadel, nach der wir suchen 1+2, wenn sie als Regex behandelt wird, "eine oder mehrere 1 gefolgt von einer 2" ... aber auf unsere folgt ein Plus!

1 + 2 als Regex

Wenn Sie also die greplEinstellung ohne Einstellung verwenden fixed, sind Ihre Nadeln versehentlich Heuhaufen, und das funktioniert versehentlich ziemlich oft. Wir können sehen, dass dies sogar für das Beispiel des OP funktioniert. Aber das ist ein latenter Fehler! Wir müssen ihm sagen, dass die Eingabe eine Zeichenfolge ist, keine Regex, wofür anscheinend gedacht fixedist. Warum behoben? Keine Ahnung, setzen Sie ein Lesezeichen für diese Antwort, da Sie sie wahrscheinlich noch fünf Mal nachschlagen müssen, bevor Sie sie auswendig lernen.

Ein paar letzte Gedanken

Je besser Ihr Code ist, desto weniger Verlauf müssen Sie kennen, um einen Sinn daraus zu ziehen. Jedes Argument kann mindestens zwei interessante Werte haben (andernfalls müsste es kein Argument sein). In den Dokumenten sind hier 9 Argumente aufgeführt, was bedeutet, dass es mindestens 2 ^ 9 = 512 Möglichkeiten gibt, es aufzurufen. Das ist eine Menge Arbeit Schreiben, Testen und Erinnern ... Entkoppeln Sie solche Funktionen (teilen Sie sie auf, entfernen Sie Abhängigkeiten voneinander, String-Dinge unterscheiden sich von Regex-Dingen von Vektor-Dingen). Einige der Optionen schließen sich auch gegenseitig aus. Geben Sie den Benutzern keine falschen Möglichkeiten zur Verwendung des Codes. Das heißt, der problematische Aufruf sollte strukturell unsinnig sein (z. B. das Übergeben einer nicht vorhandenen Option) und nicht logisch unsinnig (wo Sie müssen) eine Warnung ausgeben, um es zu erklären). Metaphorisch ausgedrückt: Das Ersetzen der Vordertür an der Seite des 10. Stockwerks durch eine Wand ist besser als das Aufhängen eines Schilds, das vor seiner Verwendung warnt, aber beides ist besser als keines. In einer Schnittstelle definiert die Funktion, wie die Argumente aussehen sollen, nicht der Aufrufer (da der Aufrufer von der Funktion abhängt und alles ableitet, womit jeder sie jemals aufrufen möchte, hängt die Funktion auch von den Aufrufern und diesem Typ ab zyklische Abhängigkeit verstopft schnell ein System und bietet niemals die erwarteten Vorteile. Seien Sie sehr vorsichtig bei zweideutigen Typen, es ist ein Designfehler, den Dinge mögen Wenn Sie auf alles schließen, mit dem jeder es jemals aufrufen möchte, hängt die Funktion auch von den Anrufern ab. Diese Art der zyklischen Abhängigkeit verstopft ein System schnell und bietet niemals die erwarteten Vorteile. Seien Sie sehr vorsichtig bei zweideutigen Typen, es ist ein Designfehler, den Dinge mögen Wenn Sie auf alles schließen, mit dem jeder es jemals aufrufen möchte, hängt die Funktion auch von den Anrufern ab. Diese Art der zyklischen Abhängigkeit verstopft ein System schnell und bietet niemals die erwarteten Vorteile. Seien Sie sehr vorsichtig bei zweideutigen Typen, es ist ein Designfehler, den Dinge mögenTRUEund 0und "abc"sind alle Vektoren.

Joshua Cheek
quelle
6
Prost auf deine Erklärung! Es scheint, dass sich R über einen langen Zeitraum entwickelt hat und mit einigen seltsamen Designentscheidungen konfrontiert ist (siehe z. B. Antworten auf diese Frage zu Werttypen ). Die Rückgabe eines Vektors von Übereinstimmungsindizes erscheint in diesem Fall jedoch angemessen, ebenso wie grepdas Filtern von Zeilen und nicht von Zellen.
Krevelen
4
"fest" bezieht sich auf die Zeichen, die einer "festen" Sequenz entsprechen.
Will Beason
32

Sie wollen grepl:

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

Verwenden Sie diese Funktion aus dem stringiPaket:

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

Einige 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
quelle
22

Kann auch mit der Bibliothek "stringr" durchgeführt werden:

> 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
quelle
20

Nur für den Fall, dass Sie auch prüfen möchten, ob eine Zeichenfolge (oder eine Reihe von Zeichenfolgen) mehrere Unterzeichenfolgen enthält, können Sie auch das '|' zwischen zwei Teilzeichenfolgen.

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

Sie erhalten

[1]  TRUE FALSE FALSE  TRUE

da das erste Wort die Teilzeichenfolge "as" hat und das letzte Wort die Teilzeichenfolge "at" enthält.

C. Zeng
quelle
Der OP-Operator war genau das, was ich brauchte! +1
Sam
10

Verwenden Sie grepoder grepl beachten Sie jedoch, ob Sie reguläre Ausdrücke verwenden möchten oder nicht .

Standardmäßig grepund verwandt wird ein regulärer Ausdruck verwendet , der übereinstimmt, kein wörtlicher Teilstring. Wenn Sie das nicht erwarten und versuchen, eine ungültige Regex zu finden, funktioniert dies nicht:

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

Verwenden Sie, um einen echten Teilstringtest durchzuführen fixed = TRUE.

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

Wenn Sie Regex wollen, großartig, aber das scheint das OP nicht zu fragen.

Chris
quelle
7

Sie können verwenden grep

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

Ähnliches Problem hier: Ermitteln Sie anhand einer Zeichenfolge und einer Liste von Schlüsselwörtern, welche der Schlüsselwörter gegebenenfalls in der Zeichenfolge enthalten sind.

Empfehlungen aus diesem Thread vorschlagen stringr‚s str_detectund grepl. Hier sind die Benchmarks aus dem microbenchmarkPaket:

Verwenden von

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

und dann

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

wir finden

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

Wie Sie sehen können, sind über 5.000 Iterationen der Schlüsselwortsuche mit str_detectund greplüber eine praktische Zeichenfolge und einen Vektor von Schlüsselwörtern greplwesentlich besser als str_detect.

Das Ergebnis ist der boolesche Vektor, rder angibt, welche der Schlüsselwörter, falls vorhanden, in der Zeichenfolge enthalten sind.

Daher empfehle ich die Verwendung, greplum festzustellen, ob Schlüsselwörter in einer Zeichenfolge enthalten sind.

Alex L.
quelle