Trích xuất một đối sánh biểu thức chính quy


111

Tôi đang cố gắng trích xuất một số từ một chuỗi.

Và làm một cái gì đó như [0-9]+trên chuỗi "aaa12xxx"và nhận được "12".

Tôi nghĩ nó sẽ giống như:

> grep("[0-9]+", "aaa12xxx", value=TRUE)
[1] "aaa12xxx"

Và sau đó tôi hình dung ra ...

> sub("[0-9]+", "\\1", "aaa12xxx")
[1] "aaaxxx"

Nhưng tôi nhận được một số hình thức phản hồi:

> sub("[0-9]+", "ARGH!", "aaa12xxx")
[1] "aaaARGH!xxx"

Tôi còn thiếu một chi tiết nhỏ.

Câu trả lời:


167

Sử dụng gói stringr mới bao bọc tất cả các biểu thức chính quy hiện có hoạt động theo một cú pháp nhất quán và thêm một số gói bị thiếu:

library(stringr)
str_locate("aaa12xxx", "[0-9]+")
#      start end
# [1,]     4   5
str_extract("aaa12xxx", "[0-9]+")
# [1] "12"

3
(gần như) chính xác những gì tôi cần, nhưng khi tôi bắt đầu nhập vào, ?str_extracttôi đã thấy str_extract_allvà cuộc sống đã tốt trở lại.
dwanderson

94

Có lẽ hơi vội vàng khi nói ' bỏ qua các chức năng tiêu chuẩn ' - tệp trợ giúp cho ?gsubcác tài liệu tham khảo cụ thể thậm chí trong 'Xem thêm':

'regmatches' để trích xuất các chuỗi con phù hợp dựa trên kết quả của 'regexpr', 'gregexpr' và 'regexec'.

Vì vậy, điều này sẽ hoạt động và khá đơn giản:

txt <- "aaa12xxx"
regmatches(txt,regexpr("[0-9]+",txt))
#[1] "12"

27

Có lẽ

gsub("[^0-9]", "", "aaa12xxxx")
# [1] "12"

15

Bạn có thể sử dụng kết hợp lười biếng của PERL regexs:

> sub(".*?([0-9]+).*", "\\1", "aaa12xx99",perl=TRUE)
[1] "12"

Cố gắng thay thế các chữ số không phải là chữ số sẽ dẫn đến lỗi trong trường hợp này.


4
Không cần PERL nếu bạn sẵn sàng sử dụng xấu hơn một chút "[^ 0-9] * ([0-9] +). *"
Jyotirmoy Bhattacharya

5

Một cách sẽ là:

test <- regexpr("[0-9]+","aaa12456xxx")

Bây giờ, hãy chú ý regexpr cung cấp cho bạn các chỉ số bắt đầu và kết thúc của chuỗi:

    > test
[1] 4
attr(,"match.length")
[1] 5

Vì vậy, bạn có thể sử dụng thông tin đó với hàm substr

substr("aaa12456xxx",test,test+attr(test,"match.length")-1)

Tôi chắc rằng có một cách thanh lịch hơn để làm điều này, nhưng đây là cách nhanh nhất mà tôi có thể tìm thấy. Ngoài ra, bạn có thể sử dụng sub / gsub để loại bỏ những gì bạn không muốn để lại những gì bạn muốn.


5

Sử dụng bắt dấu ngoặc đơn trong biểu thức chính quy và tham chiếu nhóm để thay thế. Bất cứ điều gì trong ngoặc đều được ghi nhớ. Sau đó, chúng được truy cập bởi \ 2, mục đầu tiên. Dấu gạch chéo ngược đầu tiên thoát khỏi diễn giải của dấu gạch chéo ngược trong R để nó được chuyển tới trình phân tích cú pháp biểu thức chính quy.

gsub('([[:alpha:]]+)([0-9]+)([[:alpha:]]+)', '\\2', "aaa12xxx")

2

Sử dụng strapply trong gói gsubfn. strapply giống như áp dụng ở chỗ các args là đối tượng, công cụ sửa đổi và hàm ngoại trừ đối tượng là một vectơ của chuỗi (chứ không phải là một mảng) và công cụ sửa đổi là một biểu thức chính quy (thay vì một lề):

library(gsubfn)
x <- c("xy13", "ab 12 cd 34 xy")
strapply(x, "\\d+", as.numeric)
# list(13, c(12, 34))

Điều này cho biết để khớp một hoặc nhiều chữ số (\ d +) trong mỗi thành phần của x chuyển mỗi khớp qua as.numeric. Nó trả về một danh sách có các thành phần là vectơ khớp với các thành phần tương ứng của x. Nhìn vào kết quả đầu ra, chúng ta thấy rằng thành phần đầu tiên của x có một kết quả phù hợp là 13 và thành phần thứ hai của x có hai kết quả phù hợp là 12 và 34. Xem http://gsubfn.googlecode.com để biết thêm thông tin.


1

Giải pháp khác:

temp = regexpr('\\d', "aaa12xxx");
substr("aaa12xxx", temp[1], temp[1]+attr(temp,"match.length")[1])

1

Một điểm khác biệt quan trọng giữa các phương pháp tiếp cận này là hành vi với bất kỳ phương pháp không phù hợp nào. Ví dụ: phương thức regmatches có thể không trả về một chuỗi có cùng độ dài với giá trị đầu vào nếu không có khớp ở tất cả các vị trí

> txt <- c("aaa12xxx","xyz")

> regmatches(txt,regexpr("[0-9]+",txt)) # could cause problems

[1] "12"

> gsub("[^0-9]", "", txt)

[1] "12" ""  

> str_extract(txt, "[0-9]+")

[1] "12" NA  

1

Một giải pháp cho câu hỏi này

library(stringr)
str_extract_all("aaa12xxx", regex("[[:digit:]]{1,}"))
# [[1]]
# [1] "12"

[[: digit:]] : chữ số [0-9]

{1,} : Đối sánh ít nhất 1 lần


0

Sử dụng gói unglue, chúng tôi sẽ làm như sau:

# install.packages("unglue")
library(unglue)
unglue_vec(c("aaa12xxx", "aaaARGH!xxx"), "{prefix}{number=\\d+}{suffix}", var = "number")
#> [1] "12" NA

Được tạo vào ngày 11 tháng 11 năm 2019 bởi gói reprex (v0.3.0)

Sử dụng convertđối số để tự động chuyển đổi thành một số:

unglue_vec(
  c("aaa12xxx", "aaaARGH!xxx"), 
  "{prefix}{number=\\d+}{suffix}", 
  var = "number", 
  convert = TRUE)
#> [1] 12 NA

-2

Bạn có thể viết các hàm regex của mình bằng C ++, biên dịch chúng thành một DLL và gọi chúng từ R.

    #include <regex>

    extern "C" {
    __declspec(dllexport)
    void regex_match( const char **first, char **regexStr, int *_bool)
    {
        std::cmatch _cmatch;
        const char *last = *first + strlen(*first);
        std::regex rx(*regexStr);
        bool found = false;
        found = std::regex_match(*first,last,_cmatch, rx);
        *_bool = found;
    }

__declspec(dllexport)
void regex_search_results( const char **str, const char **regexStr, int *N, char **out )
{
    std::string s(*str);
    std::regex rgx(*regexStr);
    std::smatch m;

    int i=0;
    while(std::regex_search(s,m,rgx) && i < *N) {
        strcpy(out[i],m[0].str().c_str());
        i++;
        s = m.suffix().str();
    }
}
    };

gọi trong R là

dyn.load("C:\\YourPath\\RegTest.dll")
regex_match <- function(str,regstr) {
.C("regex_match",x=as.character(str),y=as.character(regstr),z=as.logical(1))$z }

regex_match("abc","a(b)c")

regex_search_results <- function(x,y,n) {
.C("regex_search_results",x=as.character(x),y=as.character(y),i=as.integer(n),z=character(n))$z }

regex_search_results("aaa12aa34xxx", "[0-9]+", 5)

4
Điều này là hoàn toàn không cần thiết. Xem các câu trả lời của "thelatemail" hoặc "Robert" cho một giải pháp dễ dàng bên trong R.
Daniel Hoop
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.