Using shorthand character classes inside character classes in R regex - r

I have defined
vec <- "5f 110y, Fast"
and
gsub("[\\s0-9a-z]+,", "", vec)
gives "5f Fast"
I would have expected it to give "Fast" since everything before the comma should get matched by the regex.
Can anyone explain to me why this is not the case?

You should keep in mind that, in TRE regex patterns, you cannot use regex escapes like \s, \d, \w inside bracket expressions.
So, the regex in your case, "[\\s0-9a-z]+,", matches 1 or more \, s, digits and lowercase ASCII letters, and then a single ,.
You may use POSIX character classes instead, like [:space:] (any whitespaces) or [:blank:] (horizontal whitespaces):
> gsub("[[:space:]0-9a-z]+,", "", vec)
[1] " Fast"
Or, use a PCRE regex with \s and perl=TRUE argument:
> gsub("[\\s0-9a-z]+,", "", vec, perl=TRUE)
[1] " Fast"
To make \s match all Unicode whitespaces, add (*UCP) PCRE verb at the pattern start: gsub("(*UCP)[\\s0-9a-z]+,", "", vec, perl=TRUE).

Could you please try folllowing and let me know if this helps you.
vec <- c("5f 110y, Fast")
gsub(".*,","",vec)
OR
gsub("[[:alnum:]]+ [[:alnum:]]+,","",vec)

A tidyverse solution would be to use str_replace with you original regex:
library(stringr)
str_replace(vec, "[\\s0-9a-z]+,", "")

Try a different regex:
gsub("[[:blank:][:digit:][:lower:]]+,", "", vec)
#[1] " Fast"
Or, to remove the space after the comma,
gsub("[[:blank:][:digit:][:lower:]]+, ", "", vec)
#[1] "Fast"

Related

Delete string parts within delimiter

I have a string as"dfgdf" sa"2323":
a <- "as\"dfgdf\" sa\"2323\""
The delimiter (same for the start and the end) here is ". So what I want is to get a string were everything is deleted within delimiter but not delimiter itself. So the end result string should look like as"" sa""
You could match " and forget what is matched using \K
Then use a negated character class matching any char except " or a whitespace character and use lookarounds to assert " to the right.
Use perl=TRUE to enable Perl-like regular expressions.
a <- "as\"dfgdf\" sa\"2323\""
gsub('"\\K[^"\\s]+(?=")', "", a, perl=TRUE)
Output
[1] "as\"\" sa\"\""
R demo
Here is another base R option using paste0 + strsplit
s <- paste0(paste0(unlist(strsplit(a, '"\\w+"')), '""'), collapse = "")
which gives
> s
[1] "as\"\" sa\"\""
> cat(s)
as"" sa""
Here is one option with a regex lookaround to match a word (\\w+) that succeeds a double quote and precedes one as pattern and is replaced by blank ("")
cat(gsub('(?<=")\\w+(?=")', "", a, perl = TRUE), "\n")
#as"" sa""
Or without regex lookaround
cat(gsub('"\\w+"', '""', a), "\n")
#as"" sa""
I also found a way with stringr library:
library(stringr)
a <- "as\"dfgdf\" sa\"2323\""
result <- str_replace_all(a, "\".*?\"", "\"\"")
cat(result)

R / stringr: split string, but keep the delimiters in the output

I tried to search for the solution, but it appears that there is no clear one for R.
I try to split the string by the pattern of, let's say, space and capital letter and I use stringr package for that.
x <- "Foobar foobar, Foobar foobar"
str_split(x, " [:upper:]")
Normally I would get:
[[1]]
[1] "Foobar foobar," "oobar foobar"
The output I would like to get, however, should include the letter from the delimiter:
[[1]]
[1] "Foobar foobar," "Foobar foobar"
Probably there is no out of box solution in stringr like back-referencing, so I would be happy to get any help.
You may split with 1+ whitespaces that are followed with an uppercase letter:
> str_split(x, "\\s+(?=[[:upper:]])")
[[1]]
[1] "Foobar foobar," "Foobar foobar"
Here,
\\s+ - 1 or more whitespaces
(?=[[:upper:]]) - a positive lookahead (a non-consuming pattern) that only checks for an uppercase letter immediately to the right of the current location in string without adding it to the match value, thus, preserving it in the output.
Note that \s matches various whitespace chars, not just plain regular spaces. Also, it is safer to use [[:upper:]] rather than [:upper:] - if you plan to use the patterns with other regex engines (like PCRE, for example).
We could use a regex lookaround to split at the space between a , and upper case character
str_split(x, "(?<=,) (?=[A-Z])")[[1]]
#[1] "Foobar foobar," "Foobar foobar"

gsub regex in R - ignore newline symbol

Here's a reproducible example
S0 <- "\n3 4 5"
S1 <- "\n3 5"
I want to use gsub and the following regex pattern (outside of R it works - tested in regex101) to return the digits. This regex should ignore \ and n whether they occur together or not.
([^\\n])(\s{1})?
I am not looking for a way to match the digits with a fundamentally different pattern - I'd like to know how to get the above pattern to work in R. The following do not work for me
gsub("([^\\\n])(\\s{1})?", "\\1", S0)
gsub("([^[\\\]n])(\\s{1})?", "\\1", S1)
The output should be
#S0 - 345
#S1 - 3 5
Since you specifically want that regex to work you could match and optional \n (using (\n)?):
gsub("(\n)?([^\\n])(\\s{1})", "\\2", S0)
#[1] "345"
gsub("(\n)?([^\\n])(\\s{1})", "\\2", S1)
#[1] "3 5"
Note that you were right, if you use a regex tester like: https://regex101.com/ it works without the extra "(\n)?". However, I think in R you have to match more for capture groups to work properly.
Your ([^\\n])(\s{1})? pattern in regex101 (PCRE) matches different strings than the same pattern used in gsub without perl=TRUE (that is, when it is handled by the TRE regex library). They would work the same if you used perl=TRUE and use gsub("([^\\\\n])(\\s{1})?", "\\1", S1, perl=TRUE).
What is so peculair with the PCRE Regex ([^\\n])(\s{1})?
This pattern in a regex tester with PCRE option matches:
([^\\n]) - any char other than \ and n (put into Group 1)
(\s{1})? - matches and captures into Group 2 any single whitespace char, optionally, 1 or 0 times.
Note this pattern does not match any non-newline char with the first capturing group, it would match any non-newline if it were [^\n].
Now, the same regex with gsub will be
gsub("([^\n])(\\s{1})?", "\\1", S1) # OR
gsub("([^\\\\n])(\\s{1})?", "\\1", S1, perl=TRUE)
Why different number of backslashes? Because the first regex is handled with TRE regex library and in these patterns, inside bracket expressions, no regex escapes are parsed as such, the \ and n are treated as 2 separate chars. In a PCRE pattern, the one with perl=TRUE, the [...] are called character classes and inside them, you can define regex escapes, and thus the \ regex escape char should be doubled (that is, inside the R string literal it should be quadrupled as you need a \ to escape \ for the R engine to "see" a backslash).
Actually, if you want to match a newline, you just need to use \n in the regex pattern, you may either use "\n" or "\\n" as both TRE and PCRE regex engines parse LF and a \n regex escape as a newline char matching pattern. These four are equivalent:
gsub("\n([^\n])(\\s{1})?", "\\1", S1)
gsub("\\n([^\n])(\\s{1})?", "\\1", S1)
gsub("\n([^\\\\n])(\\s{1})?", "\\1", S1, perl=TRUE)
gsub("\\n([^\\\\n])(\\s{1})?", "\\1", S1, perl=TRUE)
If the \n must be optional, just add ? quantifier after it, no need wrapping it with a group:
gsub("\n?([^\n])(\\s{1})?", "\\1", S1)
^
And simplifying it further:
gsub("\n?([^\n])\\s?", "\\1", S1)
And also, if by [^\n] you want to match any char but a newline, just use . with (?n) inline modifier:
gsub("(?n)(.)(\\s{1})?", "\\1", S1)
See R demo online.
A couple of issues. The is not a backslash in your S object (it's an escape-operator rather than a character) and there is a predefined digit character class that can be negated:
gsub("[^[:digit:]]", "", S)
[1] "345"
If in the other hand you wanted to exclude the newline character and the spaces, it would be done by removing one of the escape operators, since they are not needed except for the small group of special characters that exist in the character class context:
gsub("[\n ]", "", S)
[1] "345"

Removing the second "|" on the last position

Here are some examples from my data:
a <-c("sp|Q9Y6W5|","sp|Q9HB90|,sp|Q9NQL2|","orf|NCBIAAYI_c_1_1023|",
"orf|NCBIACEN_c_10_906|,orf|NCBIACEO_c_5_1142|",
"orf|NCBIAAYI_c_258|,orf|aot172_c_6_302|,orf|aot180_c_2_405|")
For a: The individual strings can contain even more entries of "sp|" and "orf"
The results have to be like this:
[1] "sp|Q9Y6W5" "sp|Q9HB90,sp|Q9NQL2" "orf|NCBIAAYI_c_1_1023"
"orf|NCBIACEN_c_10_906,orf|NCBIACEO_c_5_1142"
"orf|NCBIAAYI_c_258,orf|aot172_c_6_302,orf|aot180_c_2_405"
So the aim is to remove the last "|" for each "sp|" and "orf|" entry. It seems that "|" is a special challenge because it is a metacharacter in regular expressions. Furthermore, the length and composition of the "orf|" entries varying a lot. The only things they have in common is "orf|" or "sp|" at the beginning and that "|" is on the last position. I tried different things with gsub() but also with the stringr package or regexpr() or [:punct:], but nothing really worked. Maybe it was just the wrong combination.
We can use gsub to match the | that is followed by a , or is at the end ($) of the string and replace with blank ("")
gsub("[|](?=(,|$))", "", a, perl = TRUE)
#[1] "sp|Q9Y6W5"
#[2] "sp|Q9HB90,sp|Q9NQL2"
#[3] "orf|NCBIAAYI_c_1_1023"
#[4] "orf|NCBIACEN_c_10_906,orf|NCBIACEO_c_5_1142"
#[5] "orf|NCBIAAYI_c_258,orf|aot172_c_6_302,orf|aot180_c_2_405"
Or we split by ,', remove the last character withsubstr, andpastethelist` elements together
sapply(strsplit(a, ","), function(x) paste(substr(x, 1, nchar(x)-1), collapse=","))
An easy alternative that might work. You need to escape the "|" using "\\|".
# Input
a <-c("sp|Q9Y6W5|","sp|Q9HB90|,sp|Q9NQL2|","orf|NCBIAAYI_c_1_1023|",
"orf|NCBIACEN_c_10_906|,orf|NCBIACEO_c_5_1142|",
"orf|NCBIAAYI_c_258|,orf|aot172_c_6_302|,orf|aot180_c_2_405|")
# Expected output
b <- c("sp|Q9Y6W5", "sp|Q9HB90,sp|Q9NQL2", "orf|NCBIAAYI_c_1_1023" ,
"orf|NCBIACEN_c_10_906,orf|NCBIACEO_c_5_1142" ,
"orf|NCBIAAYI_c_258,orf|aot172_c_6_302,orf|aot180_c_2_405")
res <- gsub("\\|,", ",", gsub("\\|$", "", a))
all(res == b)
#[1] TRUE
You could construct a single regex call to gsub, but this is simple and easy to understand. The inner gsub looks for | and the end of the string and removes it. The outer gsub looks for ,| and replaces with ,.
You do not have to use a PCRE regex here as all you need can be done with the default TRE regex (if you specify perl=TRUE, the pattern is compiled with a PCRE regex engine and is sometimes slower than TRE default regex engine).
Here is the single simple gsub call:
gsub("\\|(,|$)", "\\1", a)
See the online R demo. No lookarounds are really necessary, as you see.
Pattern details
\\| - a literal | symbol (because if you do not escape it or put into a bracket expression it will denote an alternation operator, see the line below)
(,|$) - a capturing group (referenced to with \1 from the replacement pattern) matching either of the two alternatives:
, - a comma
| - or (the alternation operator)
$ - end of string anchor.
The \1 in the replacement string tells the regex engine to insert the contents stored in the capturing group #1 back into the resulting string (so, the commas are restored that way where necessary).

Remove everything after space in string

I would like to remove everything after a space in a string.
For example:
"my string is sad"
should return
"my"
I've been trying to figure out how to do this using sub/gsub but have been unsuccessful so far.
You may use a regex like
sub(" .*", "", x)
See the regex demo.
Here, sub will only perform a single search and replace operation, the .* pattern will find the first space (since the regex engine is searching strings from left to right) and .* matches any zero or more characters (in TRE regex flavor, even including line break chars, beware when using perl=TRUE, then it is not the case) as many as possible, up to the string end.
Some variations:
sub("[[:space:]].*", "", x) # \s or [[:space:]] will match more whitespace chars
sub("(*UCP)(?s)\\s.*", "", x, perl=TRUE) # PCRE Unicode-aware regex
stringr::str_replace(x, "(?s) .*", "") # (?s) will force . to match any chars
See the online R demo.
strsplit("my string is sad"," ")[[1]][1]
or, substitute everything behind the first space to nothing:
gsub(' [A-z ]*', '' , 'my string is sad')
And with numbers:
gsub('([0-9]+) .*', '\\1', c('c123123123 0320.1'))
If you want to do it with a regex:
gsub('([A-z]+) .*', '\\1', 'my string is sad')
Stringr is your friend.
library(stringr)
word("my string is sad", 1)

Resources