Extraia uma correspondência de expressão regular

111

Estou tentando extrair um número de uma string.

E faça algo como [0-9]+na corda "aaa12xxx"e pegue "12".

Eu pensei que seria algo como:

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

E então eu percebi ...

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

Mas recebi algum tipo de resposta fazendo:

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

Há um pequeno detalhe que estou perdendo.

tovare
fonte

Respostas:

167

Use o novo pacote stringr que envolve todas as expressões regulares existentes, opera em uma sintaxe consistente e adiciona algumas que estão faltando:

library(stringr)
str_locate("aaa12xxx", "[0-9]+")
#      start end
# [1,]     4   5
str_extract("aaa12xxx", "[0-9]+")
# [1] "12"
Hadley
fonte
3
(quase) exatamente o que eu precisava, mas quando comecei a digitar ?str_extract, vi que a str_extract_allvida estava boa novamente.
dwanderson
94

Provavelmente é um pouco precipitado dizer ' ignorar as funções padrão ' - o arquivo de ajuda para ?gsubreferências específicas em 'Consulte também':

'regmatches' para extrair substrings correspondentes com base nos resultados de 'regexpr', 'gregexpr' e 'regexec'.

Portanto, isso funcionará e é bastante simples:

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

Talvez

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

Você pode usar a correspondência lenta de regexs PERL:

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

Tentar substituir não dígitos levará a um erro neste caso.

Jyotirmoy Bhattacharya
fonte
4
Não precisa de PERL se você estiver disposto a usar o ligeiramente mais feio "[^ 0-9] * ([0-9] +). *"
Jyotirmoy Bhattacharya
5

Uma maneira seria esta:

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

Agora, observe que regexpr fornece os índices inicial e final da string:

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

Então você pode usar essa informação com a função substr

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

Tenho certeza de que existe uma maneira mais elegante de fazer isso, mas foi a maneira mais rápida que encontrei. Alternativamente, você pode usar sub / gsub para retirar o que você não deseja, deixando o que deseja.

Robert
fonte
5

Use a captura de parênteses na expressão regular e referências de grupo na substituição. Qualquer coisa entre parênteses é lembrada. Em seguida, eles são acessados ​​por \ 2, o primeiro item. A primeira barra invertida escapa da interpretação da barra invertida em R para que seja passada para o analisador de expressão regular.

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

Usando o strapply no pacote gsubfn. Strapply é como apply, pois os args são objeto, modificador e função, exceto que o objeto é um vetor de strings (em vez de uma matriz) e o modificador é uma expressão regular (em vez de uma margem):

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

Isso diz para combinar um ou mais dígitos (\ d +) em cada componente de x, passando cada correspondência por as.numeric. Ele retorna uma lista cujos componentes são vetores de correspondências dos respectivos componentes de x. Olhando a saída em, vemos que o primeiro componente de x tem uma correspondência que é 13 e o segundo componente de x tem duas correspondências, que são 12 e 34. Consulte http://gsubfn.googlecode.com para obter mais informações.

G. Grothendieck
fonte
1

Outra solução:

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

Uma diferença importante entre essas abordagens é o comportamento com quaisquer não correspondências. Por exemplo, o método regmatches pode não retornar uma string do mesmo comprimento da entrada se não houver uma correspondência em todas as posições

> 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  
andyyy
fonte
1

Uma solução para esta questão

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

[[: dígito:]] : dígito [0-9]

{1,} : Corresponde pelo menos 1 vezes

Tho Vu
fonte
0

Usando o pacote unglue , faríamos o seguinte:

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

Criado em 06/11/2019 pelo pacote reprex (v0.3.0)

Use o convertargumento para converter para um número automaticamente:

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

Você pode escrever suas funções regex com C ++, compilá-las em uma DLL e chamá-las de 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();
    }
}
    };

chame R como

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)

fonte
4
Isso é completamente desnecessário. Veja as respostas de "thelatemail" ou "Robert" para uma solução fácil dentro de R.
Daniel Hoop