Kiểm tra nếu các ký tự nằm trong một chuỗi


279

Tôi đang cố xác định xem một chuỗi có phải là tập con của một chuỗi khác không. Ví dụ:

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

Tôi muốn trả về ĐÚNG nếu "giá trị" xuất hiện như một phần của chuỗi "ký tự". Trong trường hợp sau, tôi muốn trả về false:

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

12
Câu trả lời được chấp nhận là sai, bạn cần thêm fixed=TRUE, nếu không, bạn đang coi nó là biểu thức chính quy thay vì chuỗi. Xem câu trả lời của tôi từ tháng 10 năm 2016.
Joshua Cheek

@JoshuaCheek Trừ khi bạn có các ký tự đặc biệt trong mẫu của mình, regex sẽ trả về kết quả giống như đã sửa.
dùng3932000

1
Chắc chắn, nhưng bạn chỉ có thể biết rằng nếu bạn vượt qua nó theo nghĩa đen. Mặt khác, bạn sẽ không biết những ký tự nào trong mẫu, vì vậy bạn sẽ sử dụng fixed=TRUEhoặc bạn có một lỗi sẽ làm xáo trộn dữ liệu của bạn một cách lặng lẽ và tinh tế.
Joshua Cheek

Câu trả lời:


387

Sử dụng greplchức năng

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

Sử dụng ?greplđể tìm hiểu thêm.


8
Đối với trường hợp đơn giản này, việc thêm fixed = TRUE có thể cải thiện hiệu suất (giả sử rằng bạn sẽ thực hiện nhiều tính toán này).
Greg Snow

1
@Josh O'brien, bài đăng đó đã so sánh việc tìm (đếm) tất cả các trận đấu trong một chuỗi dài duy nhất, hãy thử tìm 1 trận đấu trong một chuỗi các chuỗi ngắn hơn : vec <- replicate(100000, paste( sample(letters, 10, replace=TRUE), collapse='') ).
Greg Snow

2
@GregSnow - Đã thử system.time(a <- grepl("abc", vec))system.time(a <- grepl("abc", vec, fixed=TRUE)), và fixed=TRUEvẫn còn, nếu bất cứ điều gì chậm hơn một chút. Sự khác biệt không đáng kể với các chuỗi ngắn này, nhưng fixed=TRUEdường như vẫn không nhanh hơn. Tuy nhiên, cảm ơn bạn đã chỉ ra rằng đó là chuỗi dài fixed=TRUEthực sự gây ấn tượng.
Josh O'Brien

2
grepl (mẫu, x) ít nhất trong năm 2017
JMR

2
Đây không phải là câu trả lời được chấp nhận, bởi vì giá trị sẽ được hiểu là một mẫu biểu thức chính quy. fixed = TRUE phải luôn được sử dụng trừ khi bạn biết chuỗi bạn đang tìm kiếm sẽ không giống như mẫu regex. Câu trả lời của Joshua Creek dưới đây có một lời giải thích rất rõ ràng về điều này, và nên là câu trả lời được chấp nhận.
bhaller

159

Câu trả lời

Thở dài, tôi mất 45 phút để tìm câu trả lời cho câu hỏi đơn giản này. Câu trả lời là: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

Diễn dịch

grepđược đặt tên theo tệp thực thi linux, bản thân nó là từ viết tắt của " G lobal R egular E xpression P rint", nó sẽ đọc các dòng đầu vào và sau đó in chúng nếu chúng khớp với các đối số bạn đưa ra. "Toàn cầu" có nghĩa là trận đấu có thể xảy ra ở bất kỳ đâu trên dòng đầu vào, tôi sẽ giải thích "Biểu thức chính quy" bên dưới, nhưng ý tưởng là cách thông minh hơn để khớp chuỗi (R gọi đây là "ký tự", ví dụ class("abc")) và "In "Bởi vì đó là một chương trình dòng lệnh, phát ra đầu ra có nghĩa là nó in ra chuỗi đầu ra của nó.

Bây giờ, grepchương trình về cơ bản là một bộ lọc, từ dòng đầu vào, đến dòng đầu ra. Và có vẻ như grepchức năng của R tương tự sẽ có một loạt các đầu vào. Vì những lý do mà tôi hoàn toàn không biết (tôi chỉ bắt đầu chơi với R khoảng một giờ trước), nó trả về một vectơ chỉ mục khớp, thay vì danh sách các trận đấu.

Nhưng, trở lại câu hỏi ban đầu của bạn, điều chúng tôi thực sự muốn là liệu chúng tôi có tìm thấy kim trong đống cỏ khô hay không, một giá trị đúng / sai. Họ rõ ràng đã quyết định đặt tên hàm này grepl, như trong "grep" nhưng với giá trị trả về " L ogical" (họ gọi các giá trị logic đúng và sai, ví dụ class(TRUE)).

Vì vậy, bây giờ chúng ta biết tên đến từ đâu và nó phải làm gì. Hãy quay trở lại Biểu thức thông thường. Các đối số, mặc dù chúng là các chuỗi, chúng được sử dụng để xây dựng các biểu thức chính quy (từ đó: regex). Một regex là một cách để khớp với một chuỗi (nếu định nghĩa này làm bạn khó chịu, hãy để nó đi). Ví dụ: regex akhớp với nhân vật "a", regex a*khớp với nhân vật "a"0 lần trở lên và regex a+sẽ khớp với nhân vật "a"1 lần trở lên. Do đó trong ví dụ trên, kim mà chúng ta đang tìm kiếm 1+2, khi được coi là biểu thức chính quy, có nghĩa là "một hoặc nhiều 1 theo sau là 2" ... nhưng chúng ta được theo sau bởi một dấu cộng!

1 + 2 dưới dạng biểu thức chính

Vì vậy, nếu bạn sử dụng greplcài đặt không có cài đặt fixed, kim của bạn sẽ vô tình trở thành đống cỏ khô và điều đó sẽ vô tình hoạt động khá thường xuyên, chúng ta có thể thấy nó thậm chí hoạt động cho ví dụ của OP. Nhưng đó là một lỗi tiềm ẩn! Chúng ta cần nói với nó rằng đầu vào là một chuỗi, không phải là biểu thức chính quy, rõ ràng fixedlà để làm gì . Tại sao phải sửa? Không có manh mối, đánh dấu câu trả lời này b / c có lẽ bạn sẽ phải tìm kiếm nó thêm 5 lần nữa trước khi bạn ghi nhớ nó.

Một vài suy nghĩ cuối cùng

Mã của bạn càng tốt thì càng ít lịch sử bạn phải biết để hiểu ý nghĩa của nó. Mọi đối số có thể có ít nhất hai giá trị thú vị (nếu không nó không cần phải là đối số), tài liệu liệt kê 9 đối số ở đây, có nghĩa là có ít nhất 2 ^ 9 = 512 cách để gọi nó, đó là rất nhiều công việc để viết, kiểm tra và ghi nhớ ... tách các hàm như vậy (tách chúng ra, loại bỏ các phụ thuộc lẫn nhau, các chuỗi ký tự khác với các thứ regex khác với các thứ vectơ). Một số tùy chọn cũng loại trừ lẫn nhau, không cung cấp cho người dùng cách sử dụng mã không chính xác, nghĩa là việc gọi có vấn đề phải có cấu trúc vô nghĩa (chẳng hạn như chuyển một tùy chọn không tồn tại), không phải là vô lý (nơi bạn phải phát ra một cảnh báo để giải thích nó). Nói một cách ẩn dụ: Thay thế cửa trước ở phía bên của tầng 10 bằng một bức tường tốt hơn là treo một tấm biển cảnh báo chống lại việc sử dụng nó, nhưng một trong hai tốt hơn là không. Trong một giao diện, hàm xác định các đối số sẽ trông như thế nào, chứ không phải người gọi (vì người gọi phụ thuộc vào chức năng, suy ra mọi thứ mà mọi người có thể muốn gọi nó làm cho hàm cũng phụ thuộc vào người gọi và loại này phụ thuộc theo chu kỳ sẽ nhanh chóng làm tắc nghẽn một hệ thống và không bao giờ cung cấp những lợi ích mà bạn mong đợi). Hãy cảnh giác với các loại tương đương, đó là một lỗ hổng thiết kế mà mọi thứ như suy ra mọi thứ mà mọi người có thể muốn gọi nó làm cho chức năng cũng phụ thuộc vào người gọi, và loại phụ thuộc theo chu kỳ này sẽ nhanh chóng làm tắc nghẽn hệ thống và không bao giờ cung cấp lợi ích bạn mong đợi). Hãy cảnh giác với các loại tương đương, đó là một lỗ hổng thiết kế mà mọi thứ như suy ra mọi thứ mà mọi người có thể muốn gọi nó làm cho chức năng cũng phụ thuộc vào người gọi, và loại phụ thuộc theo chu kỳ này sẽ nhanh chóng làm tắc nghẽn hệ thống và không bao giờ cung cấp lợi ích bạn mong đợi). Hãy cảnh giác với các loại tương đương, đó là một lỗ hổng thiết kế mà mọi thứ nhưTRUE0"abc"tất cả đều vectơ.


6
Chúc mừng cho lời giải thích của bạn! Nó xuất hiện R phát triển trong một thời gian dài và bị mắc kẹt với một số lựa chọn thiết kế kỳ lạ (xem ví dụ: câu trả lời cho câu hỏi này về các loại giá trị ). Tuy nhiên, việc trả về một vectơ chỉ mục khớp có vẻ thích hợp trong trường hợp này, cũng như greplọc các hàng, không phải các ô.
krevelen

4
"fixed" dùng để chỉ các ký tự khớp với chuỗi "cố định".
Will Bory

32

Bạn muốn grepl:

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

27

Sử dụng chức năng này từ stringigói:

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

Một số điểm chuẩn:

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

Ngoài ra, có thể được thực hiện bằng thư viện "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

Chỉ trong trường hợp bạn cũng muốn kiểm tra xem một chuỗi (hoặc một chuỗi) có chứa nhiều chuỗi con hay không, bạn cũng có thể sử dụng '|' giữa hai chuỗi con.

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

Bạn sẽ nhận được

[1]  TRUE FALSE FALSE  TRUE

vì từ thứ 1 có chuỗi con "là" và từ cuối cùng chứa chuỗi con "tại"


Toán tử OR chính xác là những gì tôi cần! +1
Sam

10

Sử dụng grephoặc grepl nhưng lưu ý xem bạn có muốn sử dụng biểu thức thông thường hay không .

Theo mặc định, grepvà có liên quan lấy một biểu thức chính quy để khớp, không phải là một chuỗi con theo nghĩa đen. Nếu bạn không mong đợi điều đó và bạn cố gắng khớp với biểu thức chính quy không hợp lệ, thì nó không hoạt động:

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

Để làm một bài kiểm tra chuỗi con thực sự, sử dụng fixed = TRUE.

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

Nếu bạn thực sự muốn regex, thật tuyệt, nhưng đó không phải là điều mà OP dường như đang yêu cầu.


7

Bạn có thể dùng grep

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

0

Vấn đề tương tự ở đây: Đưa ra một chuỗi và một danh sách các từ khóa, phát hiện từ khóa nào, nếu có, trong các từ khóa được chứa trong chuỗi.

Các khuyến nghị từ chủ đề này đề nghị stringr's str_detectgrepl. Dưới đây là điểm chuẩn từmicrobenchmark gói:

Sử dụng

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

và sau đó

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

chúng ta tìm thấy

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

Như bạn có thể thấy, hơn 5.000 lần lặp lại tìm kiếm từ khóa bằng cách sử dụng str_detectgreplqua một chuỗi và vectơ từ khóa greplthực tế , hoạt động tốt hơn một chút so vớistr_detect .

Kết quả là vectơ boolean r xác định từ khóa nào, nếu có, trong các từ khóa được chứa trong chuỗi.

Do đó, tôi khuyên bạn nên sử dụng greplđể xác định xem có bất kỳ từ khóa nào trong một chuỗi không.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.