Estrai una corrispondenza di espressione regolare


111

Sto cercando di estrarre un numero da una stringa.

E fai qualcosa come [0-9]+sulla corda "aaa12xxx"e prendi "12".

Ho pensato che sarebbe stato qualcosa del tipo:

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

E poi ho pensato ...

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

Ma ho ottenuto una qualche forma di risposta facendo:

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

C'è un piccolo dettaglio che mi manca.

Risposte:


167

Usa il nuovo pacchetto stringr che racchiude tutte le espressioni regolari esistenti opera in una sintassi coerente e ne aggiunge alcune mancanti:

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

3
(quasi) esattamente quello di cui avevo bisogno, ma quando ho iniziato a digitare ?str_extractho visto str_extract_alle la vita era di nuovo bella.
dwanderson

94

Probabilmente è un po 'affrettato dire' ignora le funzioni standard '- il file della guida ?gsubanche per riferimenti specifici in' Vedi anche ':

"regmatches" per l'estrazione di sottostringhe corrispondenti in base ai risultati di "regexpr", "gregexpr" e "regexec".

Quindi funzionerà ed è abbastanza semplice:

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

27

Può essere

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

15

Puoi utilizzare la corrispondenza pigra delle espressioni regolari PERL:

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

Cercare di sostituire le non cifre in questo caso porterà a un errore.


4
Non hai bisogno del PERL se sei disposto a usare il leggermente più brutto "[^ 0-9] * ([0-9] +). *"
Jyotirmoy Bhattacharya

5

Un modo sarebbe questo:

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

Ora, nota regexpr ti dà gli indici iniziale e finale della stringa:

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

Quindi puoi usare queste informazioni con la funzione substr

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

Sono sicuro che ci sia un modo più elegante per farlo, ma questo è stato il modo più veloce che ho trovato. In alternativa, puoi usare sub / gsub per eliminare ciò che non vuoi lasciare quello che vuoi.


5

Utilizzare l'acquisizione delle parentesi nell'espressione regolare e dei riferimenti di gruppo nella sostituzione. Qualsiasi cosa tra parentesi viene ricordata. Quindi vengono acceduti da \ 2, il primo elemento. Il primo backslash sfugge all'interpretazione del backslash in R in modo che venga passato al parser di espressioni regolari.

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

2

Usando strapply nel pacchetto gsubfn. strapply è come apply in quanto gli argomenti sono oggetto, modificatore e funzione tranne che l'oggetto è un vettore di stringhe (piuttosto che un array) e il modificatore è un'espressione regolare (piuttosto che un margine):

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

Questo dice di abbinare una o più cifre (\ d +) in ogni componente di x passando ogni corrispondenza per as.numeric. Restituisce una lista i cui componenti sono vettori di corrispondenze dei rispettivi componenti di x. Guardando l'output vediamo che il primo componente di x ha una corrispondenza che è 13 e il secondo componente di x ha due corrispondenze che sono 12 e 34. Vedi http://gsubfn.googlecode.com per maggiori informazioni.


1

Un'altra soluzione:

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

1

Una differenza importante tra questi approcci al comportamento con eventuali non corrispondenze. Ad esempio, il metodo regmatches potrebbe non restituire una stringa della stessa lunghezza dell'input se non c'è una corrispondenza in tutte le posizioni

> 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

Una soluzione per questa domanda

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

[[: digit:]] : cifra [0-9]

{1,} : corrisponde almeno una volta


0

Utilizzando il pacchetto unglue faremmo quanto segue:

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

Creato il 06/11/2019 dal pacchetto reprex (v0.3.0)

Usa l' convertargomento per convertire automaticamente in un numero:

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

-2

Puoi scrivere le tue funzioni regex con C ++, compilarle in una DLL e chiamarle da 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();
    }
}
    };

chiamare in R come

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
Questo è completamente inutile. Vedi le risposte di "thelatemail" o "Robert" per una facile soluzione all'interno di R.
Daniel Hoop
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.