Extraer una coincidencia de expresión regular

111

Estoy intentando extraer un número de una cadena.

Y haz algo como [0-9]+en la cuerda "aaa12xxx"y consigue "12".

Pensé que sería algo como:

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

Y luego pensé ...

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

Pero obtuve alguna forma de respuesta haciendo:

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

Hay un pequeño detalle que me falta.

tovare
fuente

Respuestas:

167

Use el nuevo paquete stringr que envuelve todas las expresiones regulares existentes que operan en una sintaxis consistente y agrega algunas que faltan:

library(stringr)
str_locate("aaa12xxx", "[0-9]+")
#      start end
# [1,]     4   5
str_extract("aaa12xxx", "[0-9]+")
# [1] "12"
Hadley
fuente
3
(casi) exactamente lo que necesitaba, pero cuando comencé a escribir ?str_extractvi que la str_extract_allvida era buena nuevamente.
dwanderson
94

Probablemente sea un poco apresurado decir ' ignorar las funciones estándar ', el archivo de ayuda ?gsubincluso para referencias específicas en 'Ver también':

'regmatches' para extraer subcadenas coincidentes según los resultados de 'regexpr', 'gregexpr' y 'regexec'.

Entonces esto funcionará, y es bastante simple:

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

Tal vez

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

Puede usar la coincidencia perezosa de las expresiones regulares PERL:

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

En este caso, intentar sustituir los que no son dígitos dará lugar a un error.

Jyotirmoy Bhattacharya
fuente
4
No necesita PERL si está dispuesto a usar el ligeramente más feo "[^ 0-9] * ([0-9] +). *"
Jyotirmoy Bhattacharya
5

Una forma sería esta:

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

Ahora, observe que regexpr le da los índices inicial y final de la cadena:

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

Entonces puedes usar esa información con la función substr

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

Estoy seguro de que hay una forma más elegante de hacer esto, pero esta fue la forma más rápida que pude encontrar. Alternativamente, puede usar sub / gsub para eliminar lo que no desea y dejar lo que sí desea.

Robert
fuente
5

Utilice la captura de paréntesis en la expresión regular y referencias de grupo en el reemplazo. Todo lo que esté entre paréntesis se recuerda. Luego, se accede a ellos mediante \ 2, el primer elemento. La primera barra invertida escapa a la interpretación de la barra invertida en R para que se pase al analizador de expresiones regulares.

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

Usando strapply en el paquete gsubfn. strapply es como aplicar en que los argumentos son objeto, modificador y función, excepto que el objeto es un vector de cadenas (en lugar de una matriz) y el modificador es una expresión regular (en lugar de un margen):

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

Esto dice que coincida con uno o más dígitos (\ d +) en cada componente de x pasando cada coincidencia a través de as.numeric. Devuelve una lista cuyos componentes son vectores de coincidencias de los componentes respectivos de x. Al observar la salida, vemos que el primer componente de x tiene una coincidencia que es 13 y el segundo componente de x tiene dos coincidencias que son 12 y 34. Consulte http://gsubfn.googlecode.com para obtener más información.

G. Grothendieck
fuente
1

Otra solución:

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

Una diferencia importante entre estos enfoques es el comportamiento con cualquier no coincidencia. Por ejemplo, es posible que el método regmatches no devuelva una cadena de la misma longitud que la entrada si no hay una coincidencia en todas las posiciones

> 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
fuente
1

Una solución para esta pregunta

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

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

{1,} : coincide al menos 1 veces

Tho Vu
fuente
0

Usando el paquete unglue haríamos lo siguiente:

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

Creado el 2019-11-06 por el paquete reprex (v0.3.0)

Utilice el convertargumento para convertir a un número automáticamente:

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

Puede escribir sus funciones de expresiones regulares con C ++, compilarlas en una DLL y llamarlas desde 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();
    }
}
    };

llamar a 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)

fuente
4
Esto es completamente innecesario. Vea las respuestas de "thelatemail" o "Robert" para una solución fácil dentro de R.
Daniel Hoop