regex implementation to replace group with its lowercase version - regex

Is there any implementation of regex that allow to replace group in regex with lowercase version of it?

If your regex version supports it, you can use \L, like so in a POSIX shell:
sed -r 's/(^.*)/\L\1/'

In Perl, you can do:
$string =~ s/(some_regex)/lc($1)/ge;
The /e option causes the replacement expression to be interpreted as Perl code to be evaluated, whose return value is used as the final replacement value. lc($x) returns the lowercased version of $x. (Not sure but I assume lc() will handle international characters correctly in recent Perl versions.)
/g means match globally. Omit the g if you only want a single replacement.

If you're using an editor like SublimeText or TextMate1, there's a good chance you may use
\L$1
as your replacement, where $1 refers to something from the regular expression that you put parentheses around. For example2, here's something I used to downcase field names in some SQL, getting everything to the right of the 'as' at the end of any given line. First the "find" regular expression:
(as|AS) ([A-Za-z_]+)\s*,$
and then the replacement expression:
$1 '\L$2',
If you use Vim (or presumably gvim), then you'll want to use \L\1 instead of \L$1, but there's another wrinkle that you'll need to be aware of: Vim reverses the syntax between literal parenthesis characters and escaped parenthesis characters. So to designate a part of the regular expression to be included in the replacement ("captured"), you'll use \( at the beginning and \) at the end. Think of \ as—instead of escaping a special character to make it a literal—marking the beginning of a special character (as with \s, \w, \b and so forth). So it may seem odd if you're not used to it, but it is actually perfectly logical if you think of it in the Vim way.
1 I've tested this in both TextMate and SublimeText and it works as-is, but some editors use \1 instead of $1. Try both and see which your editor uses.
2 I just pulled this regex out of my history. I always tweak regexen while using them, and I can't promise this the final version, so I'm not suggesting it's fit for the purpose described, and especially not with SQL formatted differently from the SQL I was working on, just that it's a specific example of downcasing in regular expressions. YMMV. UAYOR.

Several answers have noted the use of \L. However, \E is also worth knowing about if you use \L.
\L converts everything up to the next \U or \E to lowercase. ... \E turns off case conversion.
(Source: https://www.regular-expressions.info/replacecase.html )
So, suppose you wanted to use rename to lowercase part of some file names like this:
artist_-_album_-_Song_Title_to_be_Lowercased_-_MultiCaseHash.m4a
artist_-_album_-_Another_Song_Title_to_be_Lowercased_-_MultiCaseHash.m4a
you could do something like:
rename -v 's/^(.*_-_)(.*)(_-_.*.m4a)/$1\L$2\E$3/g' *

In Perl, there's
$string =~ tr/[A-Z]/[a-z]/;

Most Regex implementations allow you to pass a callback function when doing a replace, hence you can simply return a lowercase version of the match from the callback.

Related

The regular expression used in JavaScript does not work in Java

replace(/[.?+^$[\]\\(){}|-]/g, '\\$&');
But it doesn't work in Java.
So I changed the code as follows.
replace(/[.?+^$[\\]\\\\(){}|-]/g, '\\\\$&');
It doesn't work when I change it. Please help me :(
In Java, replace does not take a regex in the constructor, for that you need replaceFirst.
But as you are using the /g flag in Javascript for all replacements, you can use replaceAll.
In Javascript, this part $& in the replacement points to the full match.
So you want to replace the full match (which is one of these characters [.?+^$[\]\\(){}|-]) prepended by a \
In Java you can use $0 instead to refer to the full match.
You can also escape the opening square bracket in the character class \\[
For example
System.out.println("{test?test^}".replaceAll("[.?+^$\\[\\]\\\\()\\{}|-]", "\\\\$0"));
See a Java demo
Output
\{test\?test\^\}
The same output in Javascript
console.log("{test?test^}".replace(/[.?+^$[\]\\(){}|-]/g, '\\$&'));

Basic Vim - Search and Replace text bounded by specific characters

Say I wanted to replace :
"Christoph Waltz" = "That's a Bingo";
"Gary Coleman" = "What are you talking about, dear Willis?";
to just have :
"Christoph Waltz"
"Gary Coleman"
i.e. I want to remove all the characters including and after the = and the ;
I thought the regex for finding the pattern would be \=.*?\;. In vim, I tried :
:%s/\=.*?\;$//g
but it gave me an Invalid Command error and Nothing after \=. How do I remove the above text? Apologies, but I'm new to this.
Vim's regular expression dialect is different; its escaping is optimized for text searches. See :help perl-patterns for a comparison with Perl regular expressions. As #EvergreenTree has noted, you can influence the escaping behavior with special atoms; cp. :help /\v.
For your example, the non-greedy match is .\{-}, not .*?, and, as mentioned, you mustn't escape several literal characters:
:%s/ =.\{-};$//
(The /g flag is superfluous, too; there can be only one match anchored to the end via $.)
This is because of vim's weird handling of regexes by default. Instead of \= interpreting as a literal =, it interprets it as a special regex character. To make vim's regex system work more normally, you can prefix it with \v, which is "very magic" mode. This should do the trick:
%s/\v\=.*\;$//g
I won't explain how vim enterprets every single character in very magic mode here, but you can read about it in this help topic:
:help /magic

Regex doesn't match. Online generator does

I've want to check with a regex this kind of string:
2020_2021_01_01
I've putted it in a variable, say $session
so i do:
if [[ "$session" =~ \d{4}[_]\d{4}[_]\d{2}[_]\d{2} ]]; then
stuff
fi
you see...it doesn't work... but I don't know why....
any help?
THANKS!
The bash manual rather tersely explains that when the =~ operator "is used, the string to the right of the operator is considered an extended regular expression and matched accordingly (as in regex(3))".
Here, regex(3) is a reference to man 3 regex, which might explain what an "extended regular expression" is. A longer description would be "Posix standard extended regular expressions", and you can find the documentation for those in the Posix document. If you're using an online regular expression tester, make sure you select "Posix regular expressions".
In short, they don't include Perlisms like \d. You can write [[:digit:]] or (if you are using the C locale) [0-9].
So your regex could have been written:
([[:digit:]]{4}_){2}[[:digit:]]{2}_[[:digit:]]{2}
(there is no need to quote _). However, be aware that the =~ operator looks for a substring which matches the pattern, rather than testing whether the left-hand operator precisely matches the pattern. So you quite possibly actually wanted an anchored match:
^([[:digit:]]{4}_){2}[[:digit:]]{2}_[[:digit:]]{2}$
The backslash character is an escape character in bash shell. In your example, I think that's making the the regular expression read like this:
d{4}[_]d{4}[_]d{2}[_]d{2}
You could confirm this by testing, setting $session to dddd_dddd_dd_dd
To workaround this, to preserve the backslash character in the regular expression, you'll need to "escape" it. In your case, preceding each backslash with an "extra" backslash may do the trick. The shell will see the two backslashes, and leave the second one, as part of the string.
if [[ "$session" =~ \\d{4}[_]\\d{4}[_]\\d{2}[_]\\d{2} ]]; then
I'm not sure if there are other characters that are going to need to be escaped. This calls for a real short script, one that you can change and run, to figure out what's working and whats not. Can you match the start of the string, a single digit character, etc.
(The whole escaping thing gets funkier... inside double quotes, inside single quotes, ...)
There was a website I used to use, put in the string I wanted, and it would give me back what it needed to look like in the shell script, I don't have a link to that anymore. There's probably a regular expression tester that let's you test "bash" regular expressions.

How to recall the match side on the substitution side of your replace command?

For example, to transform foo(a,b,c) into foo(ax,bx,cx) with a single command you would need something like:
:s/[abc]/MATCHED_CHARACTERx/g
What is the correct syntax for this substitution?
With escaped parentheses \(...\) in the search pattern you can mark sections that you want to use in the replace pattern. Each section in parentheses can be accessed by \1, \2, \3.... \0 matches the whole search pattern.
So, for your example, you could either use
:%s/[abc]/\0x/g
or, to be more specific,
:%s/foo(\(.\),\(.\),\(.\))/foo(\1x,\2x,\3x)/g
The second pattern will match only foo(<any>,<any>,<any>) instead of all occurrences of a,b,c.
The & is a synonym of \0 and stands for the entire match. So you can also use &x in your example replacement. This also works in many other tools, e.g. sed.

How can I substitute regexp matches and map the substitutions in Perl?

I.e.:
echo H#97llo | MagicPerlCommand
Stdout:
Hallo
were MagicPerlCommand is something like
perl -pnle "s/#(\d+)/chr(\1)/ge"
(but that doesn't work).
Change \1 to $1 in your MagicPerlCommand. The \digit backreference style doesn't t work when the replacement expression is evaluated (i.e. s///e).
That worked for me on Windows and Linux.
As per the j_random_hacker answer, you must use $1 rather than \1.
This is because using the '/e' modifier to the regex means the right hand half is just another normal Perl expression, and not a regex substitution. Since it's Perl, you've got to use Perl's syntax for the bracket reference, and not the usual regex syntax.