Reputation: 4087
I'm trying to extract a number from a string.
And do something like [0-9]+
on the string "aaa12xxx"
and get "12"
.
I thought it would be something like:
> grep("[0-9]+", "aaa12xxx", value=TRUE)
[1] "aaa12xxx"
And then I figured...
> sub("[0-9]+", "\\1", "aaa12xxx")
[1] "aaaxxx"
But I got some form of response doing:
> sub("[0-9]+", "ARGH!", "aaa12xxx")
[1] "aaaARGH!xxx"
There's a small detail I'm missing.
Upvotes: 136
Views: 136657
Reputation: 9486
While you said, you want to extract "12"
from "aaa12xxx"
, it seems that you actually want 12
. In such cases, strcapture
from the preinstalled utils
package is a very safe & powerful solution:
strcapture(pattern = "[^\\d]*(\\d+)[^\\d]*", x = "aaa12xxx", proto = list(my_val = integer()), perl = TRUE)
#> my_val
#> 1 12
Created on 2023-07-12 by the reprex package (v2.0.1)
Upvotes: 0
Reputation: 50704
For your specific case you could remove all not numbers:
gsub("[^0-9]", "", "aaa12xxxx")
# [1] "12"
It won't work in more complex cases
gsub("[^0-9]", "", "aaa12xxxx34")
# [1] "1234"
Upvotes: 31
Reputation: 1354
A solution for this question
library(stringr)
str_extract_all("aaa12xxx", regex("[[:digit:]]{1,}"))
# [[1]]
# [1] "12"
[[:digit:]]: digit [0-9]
{1,}: Matches at least 1 times
Upvotes: 3
Reputation: 47300
Using the package unglue we would do the following:
# install.packages("unglue")
library(unglue)
unglue_vec(c("aaa12xxx", "aaaARGH!xxx"), "{prefix}{number=\\d+}{suffix}", var = "number")
#> [1] "12" NA
Created on 2019-11-06 by the reprex package (v0.3.0)
Use the convert
argument to convert to a number automatically :
unglue_vec(
c("aaa12xxx", "aaaARGH!xxx"),
"{prefix}{number=\\d+}{suffix}",
var = "number",
convert = TRUE)
#> [1] 12 NA
Upvotes: 1
Reputation:
You could write your regex functions with C++, compile them into a DLL and call them from 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();
}
}
};
call in R as
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)
Upvotes: -3
Reputation: 1015
One important difference between these approaches the the behaviour with any non-matches. For example, the regmatches method may not return a string of the same length as the input if there is not a match in all 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
Upvotes: 4
Reputation: 808
Another solution:
temp = regexpr('\\d', "aaa12xxx");
substr("aaa12xxx", temp[1], temp[1]+attr(temp,"match.length")[1])
Upvotes: 2
Reputation: 93813
It is probably a bit hasty to say 'ignore the standard functions' - the help file for ?gsub
even specifically references in 'See also':
‘regmatches’ for extracting matched substrings based on the results of ‘regexpr’, ‘gregexpr’ and ‘regexec’.
So this will work, and is fairly simple:
txt <- "aaa12xxx"
regmatches(txt,regexpr("[0-9]+",txt))
#[1] "12"
Upvotes: 129
Reputation: 1458
Use capturing parentheses in the regular expression and group references in the replacement. Anything in parentheses gets remembered. Then they're accessed by \2, the first item. The first backslash escapes the backslash's interpretation in R so that it gets passed to the regular expression parser.
gsub('([[:alpha:]]+)([0-9]+)([[:alpha:]]+)', '\\2', "aaa12xxx")
Upvotes: 6
Reputation: 146
Using strapply in the gsubfn package. strapply is like apply in that the args are object, modifier and function except that the object is a vector of strings (rather than an array) and the modifier is a regular expression (rather than a margin):
library(gsubfn)
x <- c("xy13", "ab 12 cd 34 xy")
strapply(x, "\\d+", as.numeric)
# list(13, c(12, 34))
This says to match one or more digits (\d+) in each component of x passing each match through as.numeric. It returns a list whose components are vectors of matches of respective components of x. Looking the at output we see that the first component of x has one match which is 13 and the second component of x has two matches which are 12 and 34. See http://gsubfn.googlecode.com for more info.
Upvotes: 2
Reputation: 103898
Use the new stringr package which wraps all the existing regular expression operates in a consistent syntax and adds a few that are missing:
library(stringr)
str_locate("aaa12xxx", "[0-9]+")
# start end
# [1,] 4 5
str_extract("aaa12xxx", "[0-9]+")
# [1] "12"
Upvotes: 196
Reputation: 9587
You can use PERL regexs' lazy matching:
> sub(".*?([0-9]+).*", "\\1", "aaa12xx99",perl=TRUE)
[1] "12"
Trying to substitute out non-digits will lead to an error in this case.
Upvotes: 17
Reputation: 848
One way would be this:
test <- regexpr("[0-9]+","aaa12456xxx")
Now, notice regexpr gives you the starting and ending indices of the string:
> test
[1] 4
attr(,"match.length")
[1] 5
So you can use that info with substr function
substr("aaa12456xxx",test,test+attr(test,"match.length")-1)
I'm sure there is a more elegant way to do this, but this was the fastest way I could find. Alternatively, you can use sub/gsub to strip out what you don't want to leave what you do want.
Upvotes: 5