Extraire une correspondance d'expression régulière

112

J'essaye d'extraire un nombre d'une chaîne.

Et faites quelque chose comme [0-9]+sur la chaîne "aaa12xxx"et obtenez "12".

Je pensais que ce serait quelque chose comme:

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

Et puis j'ai pensé ...

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

Mais j'ai eu une forme de réponse en faisant:

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

Il me manque un petit détail.

tovare
la source

Réponses:

167

Utilisez le nouveau package stringr qui encapsule toutes les expressions régulières existantes fonctionne dans une syntaxe cohérente et en ajoute quelques-unes qui manquent:

library(stringr)
str_locate("aaa12xxx", "[0-9]+")
#      start end
# [1,]     4   5
str_extract("aaa12xxx", "[0-9]+")
# [1] "12"
hadley
la source
3
(presque) exactement ce dont j'avais besoin, mais en commençant à taper, ?str_extractj'ai vu str_extract_allet la vie était à nouveau belle.
dwanderson
94

Il est probablement un peu hâtif de dire ' ignorer les fonctions standard ' - le fichier d'aide pour ?gsubles références même spécifiquement dans 'Voir aussi':

'regmatches' pour extraire les sous-chaînes correspondantes en fonction des résultats de 'regexpr', 'gregexpr' et 'regexec'.

Donc, cela fonctionnera, et c'est assez simple:

txt <- "aaa12xxx"
regmatches(txt,regexpr("[0-9]+",txt))
#[1] "12"
le courrier électronique
la source
27

Peut être

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

Vous pouvez utiliser la correspondance différée des expressions rationnelles PERL:

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

Essayer de remplacer des non-chiffres entraînera une erreur dans ce cas.

Jyotirmoy Bhattacharya
la source
4
Vous n'avez pas besoin de PERL si vous êtes prêt à utiliser le légèrement plus laid "[^ 0-9] * ([0-9] +). *"
Jyotirmoy Bhattacharya
5

Une façon serait la suivante:

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

Maintenant, notez que regexpr vous donne les indices de début et de fin de la chaîne:

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

Vous pouvez donc utiliser cette information avec la fonction substr

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

Je suis sûr qu'il existe un moyen plus élégant de le faire, mais c'était le moyen le plus rapide que j'ai pu trouver. Alternativement, vous pouvez utiliser sub / gsub pour supprimer ce que vous ne voulez pas laisser ce que vous voulez.

Robert
la source
5

Utilisez des parenthèses de capture dans l'expression régulière et des références de groupe dans le remplacement. Tout ce qui est entre parenthèses est mémorisé. Ensuite, ils sont accédés par \ 2, le premier élément. La première barre oblique inverse échappe à l'interprétation de la barre oblique inverse dans R afin qu'elle soit transmise à l'analyseur d'expression régulière.

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

Utilisation de strapply dans le package gsubfn. strapply est comme apply en ce que les arguments sont objet, modificateur et fonction sauf que l'objet est un vecteur de chaînes (plutôt qu'un tableau) et que le modificateur est une expression régulière (plutôt qu'une marge):

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

Cela dit de faire correspondre un ou plusieurs chiffres (\ d +) dans chaque composant de x en passant chaque correspondance par as.numeric. Il renvoie une liste dont les composants sont des vecteurs de correspondances des composants respectifs de x. En regardant la sortie, nous voyons que le premier composant de x a une correspondance qui est 13 et le deuxième composant de x a deux correspondances qui sont 12 et 34. Voir http://gsubfn.googlecode.com pour plus d'informations.

G. Grothendieck
la source
1

Une autre solution:

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

Une différence importante entre ces approches est le comportement en cas de non-concordance. Par exemple, la méthode regmatches peut ne pas renvoyer une chaîne de la même longueur que l'entrée s'il n'y a pas de correspondance dans toutes les positions

> 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
la source
1

Une solution à cette question

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

[[: chiffre:]] : chiffre [0-9]

{1,} : correspond au moins 1 fois

Tho Vu
la source
0

En utilisant le package unglue, nous ferions ce qui suit:

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

Créé le 06/11/2019 par le package reprex (v0.3.0)

Utilisez l' convertargument pour convertir automatiquement en nombre:

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

Vous pouvez écrire vos fonctions regex avec C ++, les compiler dans une DLL et les appeler depuis 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();
    }
}
    };

appeler R comme

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)

la source
4
Ceci est complètement inutile. Voir les réponses de "thelatemail" ou "Robert" pour une solution facile à l'intérieur de R.
Daniel Hoop