Extrahieren Sie eine Übereinstimmung mit regulären Ausdrücken

111

Ich versuche, eine Zahl aus einer Zeichenfolge zu extrahieren.

Und mach so etwas wie [0-9]+an der Schnur "aaa12xxx"und hol "12".

Ich dachte, es wäre so etwas wie:

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

Und dann dachte ich mir ...

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

Aber ich habe eine Antwort bekommen:

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

Es gibt ein kleines Detail, das mir fehlt.

tovare
quelle

Antworten:

167

Verwenden Sie das neue stringr-Paket, das alle vorhandenen regulären Ausdrücke umschließt, in einer konsistenten Syntax arbeitet und einige fehlende hinzufügt:

library(stringr)
str_locate("aaa12xxx", "[0-9]+")
#      start end
# [1,]     4   5
str_extract("aaa12xxx", "[0-9]+")
# [1] "12"
Hadley
quelle
3
(fast) genau das, was ich brauchte, aber als ich anfing zu tippen, ?str_extractsah ich str_extract_allund das Leben war wieder gut.
Dwanderson
94

Es ist wahrscheinlich etwas voreilig, " Standardfunktionen ignorieren" zu sagen - die Hilfedatei für ?gsubsogar spezifische Verweise in "Siehe auch":

'regmatches' zum Extrahieren übereinstimmender Teilzeichenfolgen basierend auf den Ergebnissen von 'regexpr', 'gregexpr' und 'regexec'.

Das wird also funktionieren und ist ziemlich einfach:

txt <- "aaa12xxx"
regmatches(txt,regexpr("[0-9]+",txt))
#[1] "12"
die E-Mail
quelle
27

Vielleicht

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

Sie können das Lazy Matching von PERL Regexs verwenden:

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

Der Versuch, nicht-stellige Zahlen zu ersetzen, führt in diesem Fall zu einem Fehler.

Jyotirmoy Bhattacharya
quelle
4
Benötigen Sie keine PERL, wenn Sie bereit sind, das etwas hässlichere "[^ 0-9] * ([0-9] +). *"
Jyotirmoy Bhattacharya
5

Ein Weg wäre folgender:

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

Beachten Sie, dass Sie mit regexpr die Start- und Endindizes der Zeichenfolge erhalten:

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

Sie können diese Informationen also mit der substr-Funktion verwenden

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

Ich bin sicher, es gibt einen eleganteren Weg, dies zu tun, aber dies war der schnellste Weg, den ich finden konnte. Alternativ können Sie sub / gsub verwenden, um zu entfernen, was Sie nicht möchten, und das zu lassen, was Sie möchten.

Robert
quelle
5

Verwenden Sie Erfassungsklammern im regulären Ausdruck und Gruppenreferenzen im Ersatz. Alles in Klammern wird erinnert. Dann wird auf sie über \ 2, das erste Element, zugegriffen. Der erste Backslash entgeht der Interpretation des Backslash in R, sodass er an den Parser für reguläre Ausdrücke übergeben wird.

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

Verwenden Sie Strapply im gsubfn-Paket. strapply ist insofern wie apply, als die Argumente Objekt, Modifikator und Funktion sind, außer dass das Objekt ein Vektor von Zeichenfolgen (anstelle eines Arrays) und der Modifikator ein regulärer Ausdruck (anstelle eines Randes) ist:

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

Dies besagt, dass eine oder mehrere Ziffern (\ d +) in jeder Komponente von x übereinstimmen sollen, wobei jede Übereinstimmung als numerisch durchlaufen wird. Es gibt eine Liste zurück, deren Komponenten Vektoren von Übereinstimmungen der jeweiligen Komponenten von x sind. Wenn wir uns die Ausgabe ansehen, sehen wir, dass die erste Komponente von x eine Übereinstimmung hat, die 13 ist, und die zweite Komponente von x zwei Übereinstimmungen hat, die 12 und 34 sind. Weitere Informationen finden Sie unter http://gsubfn.googlecode.com .

G. Grothendieck
quelle
1

Eine andere Lösung:

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

Ein wichtiger Unterschied zwischen diesen Ansätzen ist das Verhalten bei Nichtübereinstimmungen. Beispielsweise gibt die regmatches-Methode möglicherweise keine Zeichenfolge mit derselben Länge wie die Eingabe zurück, wenn nicht an allen Positionen eine Übereinstimmung vorliegt

> 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
quelle
0

Mit dem Paket unglue würden wir Folgendes tun:

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

Erstellt am 06.11.2019 durch das reprex-Paket (v0.3.0)

Verwenden Sie das convertArgument, um automatisch in eine Zahl umzuwandeln:

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

Sie können Ihre Regex-Funktionen mit C ++ schreiben, sie in eine DLL kompilieren und von R aus aufrufen.

    #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();
    }
}
    };

Rufen Sie R as an

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)

quelle
4
Dies ist völlig unnötig. Sehen Sie die Antworten von "thelatemail" oder "Robert" für eine einfache Lösung in R.
Daniel Hoop