Uji apakah karakter ada dalam string

279

Saya mencoba menentukan apakah suatu string adalah bagian dari string lain. Sebagai contoh:

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

Saya ingin mengembalikan TRUE jika "value" muncul sebagai bagian dari string "chars". Dalam skenario berikut, saya ingin mengembalikan false:

chars <- "test"
value <- "et"
mike
sumber
12
Jawaban yang diterima salah, Anda perlu menambahkan fixed=TRUE, jika tidak Anda memperlakukannya sebagai regex bukan string. Lihat jawaban saya dari Oktober 2016.
Joshua Cheek
@JoshuaCheek Kecuali Anda memiliki karakter khusus dalam pola Anda, regex akan mengembalikan hasil yang sama seperti yang diperbaiki.
user3932000
1
Tentu, tetapi Anda hanya bisa tahu itu jika Anda melewatinya secara literal. Jika tidak, Anda tidak akan tahu karakter apa yang ada dalam pola, sehingga Anda menggunakan fixed=TRUEatau Anda memiliki bug yang akan secara perlahan dan halus mengacaukan data Anda.
Joshua Cheek

Jawaban:

388

Gunakan greplfungsinya

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

Gunakan ?grepluntuk mencari tahu lebih lanjut.

smu
sumber
8
Untuk kasus sederhana ini menambahkan fixed = TRUE dapat meningkatkan kinerja (dengan asumsi bahwa Anda akan melakukan banyak perhitungan ini).
Greg Snow
1
@ Josh O'brien, bahwa dibandingkan temuan (penghitungan) pasca semua pertandingan dalam serangkaian panjang tunggal, mencoba menemukan 1 pertandingan dalam sekelompok string pendek: vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Snow
2
@GregSnow - Sudah mencoba system.time(a <- grepl("abc", vec))dan system.time(a <- grepl("abc", vec, fixed=TRUE)), dan fixed=TRUEmasih, jika ada yang sedikit lebih lambat. Perbedaannya tidak cukup dengan string pendek ini, tetapi fixed=TRUEmasih tampaknya tidak lebih cepat. Terima kasih telah menunjukkan, bahwa itu adalah dawai panjang yang fixed=TRUEmengambil pukulan nyata.
Josh O'Brien
2
grepl (pola, x) setidaknya pada tahun 2017
JMR
2
Ini seharusnya bukan jawaban yang diterima, karena nilai akan ditafsirkan sebagai pola regex. fixed = TRUE harus selalu digunakan kecuali Anda tahu string yang Anda cari tidak akan terlihat seperti pola regex. Jawaban Joshua Creek di bawah ini memiliki penjelasan yang sangat jelas tentang ini, dan harus menjadi jawaban yang diterima.
bhaller
159

Menjawab

Huh, butuh waktu 45 menit untuk menemukan jawaban untuk pertanyaan sederhana ini. Jawabannya adalah: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

Penafsiran

grepdinamai sesuai dengan linux executable, yang merupakan akronim dari " G lobal R egular E xpression P rint", ia akan membaca baris input dan kemudian mencetaknya jika cocok dengan argumen yang Anda berikan. "Global" berarti kecocokan dapat terjadi di mana saja pada baris input, saya akan menjelaskan "Ekspresi Reguler" di bawah, tetapi idenya adalah cara yang lebih cerdas untuk mencocokkan string (R menyebut "karakter" ini, mis. class("abc")), Dan "Mencetak "Karena ini adalah program baris perintah, memancarkan keluaran berarti mencetak ke string keluarannya.

Sekarang, grepprogram ini pada dasarnya adalah sebuah filter, dari jalur input, ke jalur output. Dan tampaknya grepfungsi R juga akan mengambil sejumlah input. Untuk alasan yang sama sekali tidak saya ketahui (saya baru mulai bermain dengan R sekitar satu jam yang lalu), ia mengembalikan vektor indeks yang cocok, bukan daftar pertandingan.

Tetapi, kembali ke pertanyaan awal Anda, yang benar-benar kami inginkan adalah mengetahui apakah kami menemukan jarum di tumpukan jerami, nilai benar / salah. Mereka tampaknya memutuskan untuk memberi nama fungsi ini grepl, seperti pada "grep" tetapi dengan nilai pengembalian " L ogical" (mereka menyebut nilai logis benar dan salah, misalnya class(TRUE)).

Jadi, sekarang kita tahu dari mana nama itu berasal dan apa yang seharusnya dilakukan. Mari kita kembali ke Ekspresi Reguler. Argumen, meskipun mereka adalah string, mereka digunakan untuk membangun ekspresi reguler (selanjutnya: regex). Regex adalah cara untuk mencocokkan string (jika definisi ini membuat Anda jengkel, biarkan saja). Misalnya, regex acocok dengan karakter "a", regex a*cocok dengan karakter "a"0 kali atau lebih, dan regex a+akan cocok dengan karakter "a"1 atau lebih kali. Karenanya dalam contoh di atas, jarum yang kita cari 1+2, ketika diperlakukan sebagai regex, berarti "satu atau lebih 1 diikuti oleh 2" ... tetapi milik kita diikuti oleh nilai tambah!

1 + 2 sebagai regex

Jadi, jika Anda menggunakan greplpengaturan tanpa fixed, jarum Anda secara tidak sengaja akan menjadi tumpukan jerami, dan itu akan secara tidak sengaja bekerja cukup sering, kita dapat melihatnya bahkan berfungsi sebagai contoh OP. Tapi itu bug yang tersembunyi! Kita perlu mengatakan bahwa inputnya adalah string, bukan regex, yang tampaknya untuk apa fixed. Mengapa diperbaiki? Tidak tahu, tandai jawaban ini b / c Anda mungkin harus mencarinya 5 kali lagi sebelum Anda menghafalnya.

Beberapa pemikiran terakhir

Semakin baik kode Anda, semakin sedikit riwayat yang harus Anda ketahui untuk memahaminya. Setiap argumen dapat memiliki setidaknya dua nilai menarik (jika tidak maka tidak perlu menjadi argumen), dokumen mendokumentasikan 9 argumen di sini, yang berarti ada setidaknya 2 ^ 9 = 512 cara untuk memohonnya, itu adalah banyak pekerjaan untuk menulis, menguji, dan mengingat ... memisahkan fungsi-fungsi tersebut (membaginya, menghapus dependensi satu sama lain, hal-hal string berbeda dari hal-hal regex berbeda dari hal-hal vektor). Beberapa opsi juga saling eksklusif, jangan beri pengguna cara yang salah untuk menggunakan kode, yaitu permintaan yang bermasalah harus secara struktural tidak masuk akal (seperti melewati opsi yang tidak ada), tidak logis tidak masuk akal (di mana Anda harus memancarkan peringatan untuk menjelaskannya). Masukkan secara metaforis: mengganti pintu depan di sisi lantai 10 dengan dinding lebih baik daripada menggantung tanda yang memperingatkan penggunaannya, tetapi keduanya lebih baik daripada tidak sama sekali. Dalam sebuah antarmuka, fungsi mendefinisikan seperti apa argumen seharusnya, bukan penelepon (karena penelepon tergantung pada fungsinya, menyimpulkan segala sesuatu yang semua orang mungkin ingin menyebutnya dengan membuat fungsi tergantung pada penelepon juga, dan jenis ini ketergantungan siklus akan dengan cepat menyumbat sistem dan tidak pernah memberikan manfaat yang Anda harapkan). Berhati-hatilah terhadap jenis-jenis penyamaran, ini adalah cacat desain yang disukai menyimpulkan segala sesuatu yang semua orang mungkin ingin menyebutnya dengan membuat fungsi tergantung pada penelepon juga, dan jenis ketergantungan siklus akan dengan cepat menyumbat sistem dan tidak pernah memberikan manfaat yang Anda harapkan). Berhati-hatilah terhadap jenis-jenis penyamaran, ini adalah cacat desain yang disukai menyimpulkan segala sesuatu yang semua orang mungkin ingin menyebutnya dengan membuat fungsi tergantung pada penelepon juga, dan jenis ketergantungan siklus akan dengan cepat menyumbat sistem dan tidak pernah memberikan manfaat yang Anda harapkan). Berhati-hatilah terhadap jenis-jenis penyamaran, ini adalah cacat desain yang disukaiTRUEdan 0dan "abc"semuanya vektor.

Joshua Cheek
sumber
6
Terima kasih atas penjelasan Anda! Tampaknya R berevolusi selama periode waktu yang lama dan terjebak dengan beberapa pilihan desain aneh (lihat misalnya jawaban untuk pertanyaan ini pada jenis nilai ). Namun, mengembalikan vektor indeks kecocokan tampaknya sesuai dalam kasus ini, seperti grepmenyaring baris, bukan sel.
krevelen
4
"fix" mengacu pada karakter yang cocok dengan urutan "fix".
Will Beason
32

Anda ingin grepl:

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

Gunakan fungsi ini dari stringipaket:

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

Beberapa tolok ukur:

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

Juga, dapat dilakukan dengan menggunakan pustaka "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
sumber
20

Jika Anda juga ingin memeriksa apakah string (atau serangkaian string) berisi beberapa sub-string, Anda juga dapat menggunakan '|' antara dua substring.

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

Kamu akan mendapatkan

[1]  TRUE FALSE FALSE  TRUE

karena kata pertama memiliki substring "sebagai", dan kata terakhir berisi substring "at"

C. Zeng
sumber
Operator OR adalah persis apa yang saya butuhkan! +1
Sam
10

Gunakan grepatau grepl tetapi waspadai apakah Anda ingin menggunakan ekspresi reguler atau tidak .

Secara default, grepdan terkait mengambil ekspresi reguler yang cocok, bukan substring literal. Jika Anda tidak mengharapkan itu, dan Anda mencoba mencocokkan pada regex yang tidak valid, itu tidak berfungsi:

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

Untuk melakukan tes substring yang benar, gunakan fixed = TRUE.

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

Jika Anda ingin regex, bagus, tapi bukan itu yang diminta OP.

Chris
sumber
7

Kamu bisa memakai grep

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

Masalah serupa di sini: Diberikan string dan daftar kata kunci, mendeteksi yang, jika ada, dari kata kunci yang terkandung dalam string.

Rekomendasi dari thread ini menyarankan stringr's str_detectdan grepl. Berikut adalah tolok ukur dari microbenchmarkpaket:

Menggunakan

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

lalu

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

kami menemukan

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

Seperti yang Anda lihat, lebih dari 5.000 iterasi pencarian kata kunci menggunakan str_detectdan grepllebih dari string praktis dan vektor kata kunci, greplberkinerja sedikit lebih baik daripada str_detect.

Hasilnya adalah vektor boolean ryang mengidentifikasi, jika ada, kata kunci yang terkandung dalam string.

Oleh karena itu, saya sarankan menggunakan grepluntuk menentukan apakah ada kata kunci dalam sebuah string.

Alex L
sumber