ทดสอบว่าอักขระอยู่ในสตริงหรือไม่


279

ฉันพยายามที่จะตรวจสอบว่าสตริงเป็นส่วนย่อยของสตริงอื่น ตัวอย่างเช่น:

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

ฉันต้องการส่งคืน TRUE หาก "value" ปรากฏเป็นส่วนหนึ่งของสตริง "chars" ในสถานการณ์ต่อไปนี้ฉันต้องการกลับเท็จ:

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

12
คำตอบที่ยอมรับนั้นผิดคุณต้องเพิ่มfixed=TRUEมิฉะนั้นคุณจะถือว่าเป็น regex แทนที่จะเป็นสตริง ดูคำตอบของฉันตั้งแต่ตุลาคม 2559
Joshua Cheek

@JoshuaCheek ยกเว้นว่าคุณมีอักขระพิเศษในรูปแบบของคุณ regex จะส่งคืนผลลัพธ์เช่นเดียวกับการแก้ไข
user3932000

1
แน่นอน แต่คุณสามารถรู้ว่าถ้าคุณผ่านมันไปตามตัวอักษร มิฉะนั้นคุณจะไม่ทราบว่าตัวละครอยู่ในรูปแบบใดดังนั้นคุณอาจใช้fixed=TRUEหรือคุณมีข้อผิดพลาดที่จะทำให้ข้อมูลของคุณเงียบลง
Joshua Cheek

คำตอบ:


388

ใช้ greplฟังก์ชั่น

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

ใช้?greplเพื่อหาข้อมูลเพิ่มเติม


8
สำหรับกรณีง่าย ๆ นี้การเพิ่มค่าคงที่ = TRUE อาจปรับปรุงประสิทธิภาพ (สมมติว่าคุณกำลังทำการคำนวณจำนวนมากเหล่านี้)
Greg Snow

1
@ Josh โอไบรอันที่โพสต์การค้นพบเมื่อเทียบ (นับ) การแข่งขันทั้งหมดในสายยาวเดียวลองหา 1 vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') )การแข่งขันในเครือของสตริงสั้น:
Greg Snow

2
@GregSnow - พยายามsystem.time(a <- grepl("abc", vec))และsystem.time(a <- grepl("abc", vec, fixed=TRUE))และfixed=TRUEยังคงอยู่ถ้ามีอะไรช้าลงเล็กน้อย ความแตกต่างไม่สามารถประเมินได้ด้วยสตริงสั้น ๆ เหล่านี้ แต่fixed=TRUEก็ดูเหมือนจะไม่เร็วกว่า ขอบคุณสำหรับการชี้ให้เห็นว่ามันอยู่ในสายยาวที่fixed=TRUEจะตีจริง
Josh O'Brien

2
grepl (pattern, x) อย่างน้อยในปี 2017
JMR

2
สิ่งนี้ไม่ควรเป็นคำตอบที่ยอมรับได้เนื่องจากค่าจะถูกตีความเป็นรูปแบบ regex fixed = TRUE ควรใช้เสมอเว้นแต่คุณจะรู้ว่าสตริงที่คุณค้นหาจะไม่เกิดขึ้นกับรูปแบบ regex คำตอบของ Joshua Creek ด้านล่างมีคำอธิบายที่ชัดเจนเกี่ยวกับเรื่องนี้และควรเป็นคำตอบที่ได้รับการยอมรับ
bhaller

159

ตอบ

ถอนหายใจฉันใช้เวลา 45 นาทีในการหาคำตอบสำหรับคำถามง่าย ๆ นี้ คำตอบคือ: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

การตีความ

grepได้รับการตั้งชื่อตามปฏิบัติการลีนุกซ์ซึ่งเป็นตัวย่อของ " G lobal R egular E xpression P rint " มันจะอ่านบรรทัดอินพุตแล้วพิมพ์พวกมันหากตรงกับอาร์กิวเมนต์ที่คุณให้ "ทั่วโลก" หมายถึงการจับคู่อาจเกิดขึ้นที่ใดก็ได้ในบรรทัดอินพุตฉันจะอธิบาย "นิพจน์ทั่วไป" ด้านล่าง แต่แนวคิดนี้เป็นวิธีที่ชาญฉลาดในการจับคู่สตริง (R เรียกอักขระนี้ว่า "" class("abc")) และ "พิมพ์ "เพราะเป็นโปรแกรมบรรทัดคำสั่งการเปล่งเอาต์พุตหมายความว่ามันพิมพ์ไปยังสตริงเอาต์พุต

ตอนนี้grepโปรแกรมนั้นเป็นตัวกรองจากบรรทัดอินพุตไปจนถึงบรรทัดเอาต์พุต และดูเหมือนว่าgrepฟังก์ชั่นของ R จะคล้ายกันกับอาร์เรย์ของอินพุต สำหรับเหตุผลที่ไม่รู้จักฉันอย่างเต็มที่ (ฉันเพิ่งเริ่มเล่นกับ R เมื่อประมาณหนึ่งชั่วโมงที่ผ่านมา) มันส่งคืนเวกเตอร์ของดัชนีที่ตรงกันแทนที่จะเป็นรายการการแข่งขัน

แต่กลับไปที่คำถามดั้งเดิมของคุณสิ่งที่เราต้องการจริงๆคือการรู้ว่าเราพบเข็มในกองหญ้าหรือไม่ เห็นได้ชัดว่าพวกเขาตัดสินใจตั้งชื่อฟังก์ชั่นนี้greplเช่นเดียวกับใน "grep" แต่มีค่าส่งคืน" L ogical" (พวกเขาเรียกค่าตรรกะจริงและเท็จเช่นclass(TRUE))

ดังนั้นตอนนี้เรารู้ว่าชื่อมาจากไหนและควรทำอย่างไร ให้กลับไปที่นิพจน์ปกติ อาร์กิวเมนต์ถึงแม้ว่าพวกเขาจะเป็นสตริงพวกเขาจะใช้ในการสร้างการแสดงออกปกติ (ต่อจากนี้ไป: regex) regex เป็นวิธีจับคู่สตริง (หากคำจำกัดความนี้ทำให้คุณหงุดหงิด ตัวอย่างเช่น regex aจับคู่อักขระ"a"regex a*จับคู่อักขระ"a"0 หรือมากกว่าและ regex a+จะจับคู่อักขระ"a"1 หรือมากกว่านั้น ดังนั้นในตัวอย่างด้านบนเข็มที่เรากำลังค้นหา1+2เมื่อถือว่าเป็น regex หมายถึง "หนึ่งหรือมากกว่า 1 ตามด้วย 2" ... แต่เราตามมาด้วยเครื่องหมายบวก!

1 + 2 เป็น regex

ดังนั้นหากคุณใช้การgreplตั้งค่าแบบไม่มีfixedเข็มเข็มของคุณจะเป็นกองฟางโดยไม่ตั้งใจและนั่นอาจทำงานได้ค่อนข้างบ่อยเราสามารถเห็นได้ว่ามันสามารถใช้งานได้กับตัวอย่างของ OP แต่นั่นเป็นข้อผิดพลาดแฝง! เราจำเป็นต้องบอกว่าอินพุตเป็นสตริงไม่ใช่ regex ซึ่งเห็นได้ชัดว่าfixedมีไว้สำหรับอะไร ทำไมต้องแก้ไข ไม่มีเงื่อนงำคั่นคำตอบนี้ b / c คุณอาจต้องค้นหาอีก 5 ครั้งก่อนที่คุณจะได้รับการจดจำ

ความคิดสุดท้ายเล็กน้อย

ยิ่งรหัสของคุณดีขึ้นเท่าไหร่ประวัติที่คุณต้องรู้ก็จะยิ่งน้อยลงเท่านั้น ทุกอาร์กิวเมนต์สามารถมีค่าที่น่าสนใจอย่างน้อยสองค่า (ไม่เช่นนั้นจะไม่จำเป็นต้องเป็นอาร์กิวเมนต์) เอกสารรายการ 9 ข้อโต้แย้งที่นี่ซึ่งหมายความว่ามีอย่างน้อย 2 ^ 9 = 512 วิธีในการเรียกใช้นั่นเป็นงานที่ต้องทำมากมาย เขียนทดสอบและจำไว้ว่า ... แยกฟังก์ชั่นดังกล่าวออก (แยกออก, ลบการพึ่งพาซึ่งกันและกัน, สิ่งสตริงจะแตกต่างจากสิ่งที่ regex แตกต่างจากสิ่งเวกเตอร์) ตัวเลือกบางตัวนั้นไม่เหมือนกันไม่ให้ผู้ใช้วิธีที่ไม่ถูกต้องในการใช้รหัสเช่นการเรียกใช้ที่มีปัญหาควรเป็นแบบไร้โครงสร้าง (เช่นผ่านตัวเลือกที่ไม่มีอยู่) ไม่ใช่แบบไร้เหตุผล (ซึ่งคุณต้อง ปล่อยคำเตือนเพื่ออธิบาย) ใส่คำเปรียบเทียบ: การแทนที่ประตูหน้าในด้านข้างของชั้น 10 ด้วยผนังจะดีกว่าการแขวนป้ายที่เตือนการใช้งาน แต่ดีกว่าอย่างใดอย่างหนึ่ง ในอินเทอร์เฟซฟังก์ชันจะกำหนดสิ่งที่อาร์กิวเมนต์ควรมีลักษณะไม่ใช่ตัวเรียก (เนื่องจากผู้เรียกใช้ขึ้นอยู่กับฟังก์ชันโดยอนุมานทุกสิ่งที่ทุกคนอาจต้องการเรียกด้วยทำให้ฟังก์ชันนั้นขึ้นอยู่กับผู้โทรด้วยเช่นกันและประเภทนี้ การขึ้นต่อกันของวัฏจักรจะทำให้ระบบเกิดการอุดตันอย่างรวดเร็วและไม่ให้ผลประโยชน์ที่คุณคาดหวัง) ระวังประเภทที่ทำให้สับสนมันเป็นข้อบกพร่องในการออกแบบที่เป็นเช่นนั้น อนุมานทุกสิ่งที่ทุกคนอาจต้องการเรียกใช้ด้วยทำให้ฟังก์ชั่นนั้นขึ้นอยู่กับผู้โทรเช่นกันและการพึ่งพาวงจรประเภทนี้จะขัดขวางระบบอย่างรวดเร็วและไม่ให้ประโยชน์ที่คุณคาดหวัง) ระวังประเภทที่ทำให้สับสนมันเป็นข้อบกพร่องในการออกแบบที่เป็นเช่นนั้น อนุมานทุกสิ่งที่ทุกคนอาจต้องการเรียกใช้ด้วยทำให้ฟังก์ชั่นนั้นขึ้นอยู่กับผู้โทรเช่นกันและการพึ่งพาวงจรประเภทนี้จะขัดขวางระบบอย่างรวดเร็วและไม่ให้ประโยชน์ที่คุณคาดหวัง) ระวังประเภทที่ทำให้สับสนมันเป็นข้อบกพร่องในการออกแบบที่เป็นเช่นนั้นTRUEและ0และ"abc"เวกเตอร์ทั้งหมด


6
ไชโยสำหรับคำอธิบายของคุณ! ปรากฏว่า R ได้รับการพัฒนามาเป็นเวลานานและติดอยู่กับตัวเลือกการออกแบบแปลก ๆ (ดูคำตอบสำหรับคำถามประเภทนี้กับประเภทค่า ) อย่างไรก็ตามการคืนค่าเวกเตอร์ของดัชนีการจับคู่นั้นน่าจะเหมาะสมในกรณีนี้เช่นเดียวกับการgrepกรองแถวไม่ใช่เซลล์
krevelen

4
"fixed" หมายถึงอักขระที่ตรงกับลำดับ "fixed"
Will Beason


27

ใช้ฟังก์ชั่นนี้จากstringiแพ็คเกจ:

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

มาตรฐานบางอย่าง:

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

นอกจากนี้ยังสามารถทำได้โดยใช้ห้องสมุด "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

ในกรณีที่คุณต้องการตรวจสอบว่าสตริง (หรือชุดสตริง) มีหลายสตริงย่อยคุณสามารถใช้ '|' ระหว่างสองสตริง

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

คุณจะได้รับ

[1]  TRUE FALSE FALSE  TRUE

เนื่องจากคำแรกมีซับสตริง "เป็น" และคำสุดท้ายประกอบด้วยซับสตริง "ที่"


ผู้ประกอบการหรือเป็นสิ่งที่ฉันต้องการ! +1
แซม

10

ใช้grepหรือgrepl แต่ต้องระวังว่าคุณต้องการใช้นิพจน์ทั่วไปหรือไม่หรือไม่

โดยค่าเริ่มต้นgrepและที่เกี่ยวข้องใช้นิพจน์ทั่วไปให้ตรงกันไม่ใช่สตริงย่อยที่แท้จริง หากคุณไม่ได้คาดหวังสิ่งนั้นและคุณพยายามจับคู่กับ regex ที่ไม่ถูกต้องมันไม่ทำงาน:

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

fixed = TRUEที่จะทำจริงการทดสอบย่อยใช้

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

หากคุณต้องการ regex ยอดเยี่ยม แต่นั่นไม่ใช่สิ่งที่ OP ต้องการให้ถาม



0

ปัญหาที่คล้ายกันที่นี่: กำหนดสตริงและรายการคำหลักตรวจสอบว่ามีคำสำคัญใดในสตริง

คำแนะนำจากกระทู้นี้ขอแนะนำstringr's และstr_detect greplนี่คือมาตรฐานจากmicrobenchmarkแพคเกจ:

การใช้

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

แล้ว

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

เราพบว่า

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

อย่างที่คุณเห็นการใช้คำค้นหามากกว่า 5,000 คำซ้ำstr_detectและgreplสตริงและเวกเตอร์คำหลักที่ใช้งานได้จริงนั้นgreplมีประสิทธิภาพดีกว่าstr_detectเล็กน้อย

ผลลัพธ์คือเวกเตอร์บูลีน rซึ่งระบุว่าคำหลักใดมีอยู่ในสตริง

ดังนั้นฉันขอแนะนำให้ใช้greplเพื่อตรวจสอบว่าคำหลักใด ๆ ที่อยู่ในสตริง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.