Regex ordinal digit and file size - regex

I have been thinking about a regular expression that can transform a list like this:
1. 10.Things.I.Hate.About.You[1999]DvDrip[Eng]-Ray 699.68 MB
2. 100.Feet.2008.DvDRip-FxM 701.14 MB
3. 11 - 14 1 286.22 MB
4. 13_going_on_30(2004)[Brizzly] 700.23 MB
...
1 523. Waz 699.93 MB
1 524. We.Own.the.Night[2007]DvDrip[Eng]-Ray 700.87 MB
1 525. Webs [2003]DVDRip[Xvid AC3[5.1]-RoCK&BlueLadyRG 1 347.70 MB
into:
10.Things.I.Hate.About.You[1999]DvDrip[Eng]-Ray,699.68 MB
100.Feet.2008.DvDRip-FxM,701.14
11 - 14,1286.22
13_going_on_30(2004)[Brizzly],700.23
...
Waz,699.93
We.Own.the.Night[2007]DvDrip[Eng]-Ray,700.87
Webs [2003]DVDRip[Xvid AC3[5.1]-RoCK&BlueLadyRG,1347.70
Assumption : The filesize is never > 9999.99MB
So far I have a partially working regex:
^[^\.]+\. (.+?) (?:([0-9])(?: ))?([0-9]+\.[0-9]{2}) MB.*$
that maps to
$1:$2$3
to complete the transformation.
I used the colon because no desktop OS would allow that in a filename, so I am safe.
I built the regex without any formal method (i.e, via using intution) and that very same intution tells me this regex is horrifically complicated and slow!
I wish RegExBuddy had a online version or something similar.
How do I build a better RegEx for the same? Hints, tips...

I use The Regex Coach.

In Perl:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
while ( <DATA> ) {
no warnings 'uninitialized';
next unless /^[^.]+\. (.+?) (?:(\d+) )?(\d+(?:.\d+)?) MB$/ ;
print "$1,$2$3\n";
}
__DATA__
1. 10.Things.I.Hate.About.You[1999]DvDrip[Eng]-Ray 699.68 MB
2. 100.Feet.2008.DvDRip-FxM 701.14 MB
3. 11 - 14 1 286.22 MB
4. 13_going_on_30(2004)[Brizzly] 700.23 MB
...
1 523. Waz 699.93 MB
1 524. We.Own.the.Night[2007]DvDrip[Eng]-Ray 700.87 MB
1 525. Webs [2003]DVDRip[Xvid AC3[5.1]-RoCK&BlueLadyRG 1 347.70 MB
Output:
C:\Temp> zcx
10.Things.I.Hate.About.You[1999]DvDrip[Eng]-Ray,699.68
100.Feet.2008.DvDRip-FxM,701.14
11 - 14,1286.22
13_going_on_30(2004)[Brizzly],700.23
Waz,699.93
We.Own.the.Night[2007]DvDrip[Eng]-Ray,700.87
Webs [2003]DVDRip[Xvid AC3[5.1]-RoCK&BlueLadyRG,1347.70

"I used the colon because no desktop OS would allow that in a filename, so I am safe."
Nice try. It is allowed under GNU/Linux.
More importantly, you have only given examples. You have not described what the regex is intended to do. You also have obviously pointless constructs, like (?: ), which could just be a single space.
Finally, it's unclear what role the colon actually plays, as it's not in your replacement text. Perhaps it would help if you told us what language you're using.

Related

LookBehind - Find string occuring after a pattern

I need help regarding a regex Query :-
C: - total 79.45 Gb - used: 33.82 Gb (43%) - free 45.63 Gb (57%)
This is my sample text . I want to find the %usage of used disk . i.e 43% in my case .
I am using lookbehind to find the occurrences after "used" keyword .
This is the pattern i am using (?<=(\bused))(.*?\(\d*%\)) . But this is giving me : 33.82 Gb (43%) as the output . I only need 43 as my output.
Can anyone please help
Try capturing only the \d* part:
(\bused)(.*?\((\d*)%\))
* *
The asterisks are where the group 3, the group you want, starts and ends.
Or you can make every other group non capturing, and get group 1:
(?:\bused)(?:.*?\((\d*)%\))
Demo
Using python3 you can write this :
import re
re_findall = re.findall("used:.*\(([0-9]*)%\) -",line)
where line is :
C: - total 79.45 Gb - used: 33.82 Gb (43%) - free 45.63 Gb (57%)
This is similar to what Sweeper say, you just only "capture" the number here using a python module.

R: a grep alternative for a file without using readLines?

Is there any function in any package that can read a text file with regex and return string numbers of found matches. Like gsubfn read.pattern can find and extract a pattern but can't return line number and grep can't read files directly. Example:
file:
.122448110000D+06 .400000000000D+01
3 15 3 23 10 0 0.0 .267305411398D-03 .161435309564D-10 .000000000000D+01
.510000000000D+02 .625000000000D-01 .440982654411D-08 .306376855997D+00
5 15 3 23 11 59 44.0 -.263226218521D-03 .488853402202D-11 .000000000000D+01
pattern: reg="^ *\\d+ +(?:[0-9]+ +){5}[.0-9]+.*$" for 2nd and 4th line match. So what I generally want is:
>file.grep(file,reg)
[1] 2 4
Is there anything of sorts? I get the general philosophy when dealing with such things is readLines and then getting creative with grep which is fine when files are not that big. But I read here many people having problems with large and not table-structured data sets, things that could be solved with such tool (or with readLines supporting regex skip parameter) and I wonder if anyone made something like that.
EDITED1
I just found another post relating to this question with an alternative solution:
grep while reading file
ORIGINAL POST
Is this what you are looking for?
library(gsubfn)
cat(" .122448110000D+06 .400000000000D+01
3 15 3 23 10 0 0.0 .267305411398D-03 .161435309564D-10 .000000000000D+01
.510000000000D+02 .625000000000D-01 .440982654411D-08 .306376855997D+00
5 15 3 23 11 59 44.0 -.263226218521D-03 .488853402202D-11 .000000000000D+01", file = "test.txt")
read.pattern(text = readLines("test.txt"), pattern = "^ *\\d+ +(?:[0-9]+ +){5}[.0-9]+.*$")

Is it possible to increment numbers using regex substitution?

Is it possible to increment numbers using regex substitution? Not using evaluated/function-based substitution, of course.
This question was inspired by another one, where the asker wanted to increment numbers in a text editor. There are probably more text editors that support regex substitution than ones that support full-on scripting, so a regex might be convenient to float around, if one exists.
Also, often I've learned neat things from clever solutions to practically useless problems, so I'm curious.
Assume we're only talking about non-negative decimal integers, i.e. \d+.
Is it possible in a single substitution? Or, a finite number of substitutions?
If not, is it at least possible given an upper bound, e.g. numbers up to 9999?
Of course it's doable given a while-loop (substituting while matched), but we're going for a loopless solution here.
This question's topic amused me for one particular implementation I did earlier. My solution happens to be two substitutions so I'll post it.
My implementation environment is solaris, full example:
echo "0 1 2 3 7 8 9 10 19 99 109 199 909 999 1099 1909" |
perl -pe 's/\b([0-9]+)\b/0$1~01234567890/g' |
perl -pe 's/\b0(?!9*~)|([0-9])(?=9*~[0-9]*?\1([0-9]))|~[0-9]*/$2/g'
1 2 3 4 8 9 10 11 20 100 110 200 910 1000 1100 1910
Pulling it apart for explanation:
s/\b([0-9]+)\b/0$1~01234567890/g
For each number (#) replace it with 0#~01234567890. The first 0 is in case rounding 9 to 10 is needed. The 01234567890 block is for incrementing. The example text for "9 10" is:
09~01234567890 010~01234567890
The individual pieces of the next regex can be described seperately, they are joined via pipes to reduce substitution count:
s/\b0(?!9*~)/$2/g
Select the "0" digit in front of all numbers that do not need rounding and discard it.
s/([0-9])(?=9*~[0-9]*?\1([0-9]))/$2/g
(?=) is positive lookahead, \1 is match group #1. So this means match all digits that are followed by 9s until the '~' mark then go to the lookup table and find the digit following this number. Replace with the next digit in the lookup table. Thus "09~" becomes "19~" then "10~" as the regex engine parses the number.
s/~[0-9]*/$2/g
This regex deletes the ~ lookup table.
Wow, turns out it is possible (albeit ugly)!
In case you do not have the time or cannot be bothered to read through the whole explanation, here is the code that does it:
$str = '0 1 2 3 4 5 6 7 8 9 10 11 12 13 19 20 29 99 100 139';
$str = preg_replace("/\d+/", "$0~", $str);
$str = preg_replace("/$/", "#123456789~0", $str);
do
{
$str = preg_replace(
"/(?|0~(.*#.*(1))|1~(.*#.*(2))|2~(.*#.*(3))|3~(.*#.*(4))|4~(.*#.*(5))|5~(.*#.*(6))|6~(.*#.*(7))|7~(.*#.*(8))|8~(.*#.*(9))|9~(.*#.*(~0))|~(.*#.*(1)))/s",
"$2$1",
$str, -1, $count);
} while($count);
$str = preg_replace("/#123456789~0$/", "", $str);
echo $str;
Now let's get started.
So first of all, as the others mentioned, it is not possible in a single replacement, even if you loop it (because how would you insert the corresponding increment to a single digit). But if you prepare the string first, there is a single replacement that can be looped. Here is my demo implementation using PHP.
I used this test string:
$str = '0 1 2 3 4 5 6 7 8 9 10 11 12 13 19 20 29 99 100 139';
First of all, let's mark all digits we want to increment by appending a marker character (I use ~, but you should probably use some crazy Unicode character or ASCII character sequence that definitely will not occur in your target string.
$str = preg_replace("/\d+/", "$0~", $str);
Since we will be replacing one digit per number at a time (from right to left), we will just add that marking character after every full number.
Now here comes the main hack. We add a little 'lookup' to the end of our string (also delimited with a unique character that does not occur in your string; for simplicity I used #).
$str = preg_replace("/$/", "#123456789~0", $str);
We will use this to replace digits by their corresponding successors.
Now comes the loop:
do
{
$str = preg_replace(
"/(?|0~(.*#.*(1))|1~(.*#.*(2))|2~(.*#.*(3))|3~(.*#.*(4))|4~(.*#.*(5))|5~(.*#.*(6))|6~(.*#.*(7))|7~(.*#.*(8))|8~(.*#.*(9))|9~(.*#.*(~0))|(?<!\d)~(.*#.*(1)))/s",
"$2$1",
$str, -1, $count);
} while($count);
Okay, what is going on? The matching pattern has one alternative for every possible digit. This maps digits to successors. Take the first alternative for example:
0~(.*#.*(1))
This will match any 0 followed by our increment marker ~, then it matches everything up to our cheat-delimiter and the corresponding successor (that is why we put every digit there). If you glance at the replacement, this will get replaced by $2$1 (which will then be 1 and then everything we matched after the ~ to put it back in place). Note that we drop the ~ in the process. Incrementing a digit from 0 to 1 is enough. The number was successfully incremented, there is no carry-over.
The next 8 alternatives are exactly the same for the digits 1to 8. Then we take care of two special cases.
9~(.*#.*(~0))
When we replace the 9, we do not drop the increment marker, but place it to the left of our the resulting 0 instead. This (combined with the surrounding loop) is enough to implement carry-over propagation. Now there is one special case left. For all numbers consisting solely of 9s we will end up with the ~ in front of the number. That is what the last alternative is for:
(?<!\d)~(.*#.*(1))
If we encounter a ~ that is not preceded by a digit (therefore the negative lookbehind), it must have been carried all the way through a number, and thus we simply replace it with a 1. I think we do not even need the negative lookbehind (because this is the last alternative that is checked), but it feels safer this way.
A short note on the (?|...) around the whole pattern. This makes sure that we always find the two matches of an alternative in the same references $1 and $2 (instead of ever larger numbers down the string).
Lastly, we add the DOTALL modifier (s), to make this work with strings that contain line breaks (otherwise, only numbers in the last line will be incremented).
That makes for a fairly simple replacement string. We simply first write $2 (in which we captured the successor, and possibly the carry-over marker), and then we put everything else we matched back in place with $1.
That's it! We just need to remove our hack from the end of the string, and we're done:
$str = preg_replace("/#123456789~0$/", "", $str);
echo $str;
> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 20 21 30 100 101 140
So we can do this entirely in regular expressions. And the only loop we have always uses the same regex. I believe this is as close as we can get without using preg_replace_callback().
Of course, this will do horrible things if we have numbers with decimal points in our string. But that could probably be taken care of by the very first preparation-replacement.
Update: I just realised, that this approach immediately extends to arbitrary increments (not just +1). Simply change the first replacement. The number of ~ you append equals the increment you apply to all numbers. So
$str = preg_replace("/\d+/", "$0~~~", $str);
would increment every integer in the string by 3.
I managed to get it working in 3 substitutions (no loops).
tl;dr
s/$/ ~0123456789/
s/(?=\d)(?:([0-8])(?=.*\1(\d)\d*$)|(?=.*(1)))(?:(9+)(?=.*(~))|)(?!\d)/$2$3$4$5/g
s/9(?=9*~)(?=.*(0))|~| ~0123456789$/$1/g
Explanation
Let ~ be a special character not expected to appear anywhere in the text.
If a character is nowhere to be found in the text, then there's no way to make it appear magically. So first we insert the characters we care about at the very end.
s/$/ ~0123456789/
For example,
0 1 2 3 7 8 9 10 19 99 109 199 909 999 1099 1909
becomes:
0 1 2 3 7 8 9 10 19 99 109 199 909 999 1099 1909 ~0123456789
Next, for each number, we (1) increment the last non-9 (or prepend a 1 if all are 9s), and (2) "mark" each trailing group of 9s.
s/(?=\d)(?:([0-8])(?=.*\1(\d)\d*$)|(?=.*(1)))(?:(9+)(?=.*(~))|)(?!\d)/$2$3$4$5/g
For example, our example becomes:
1 2 3 4 8 9 19~ 11 29~ 199~ 119~ 299~ 919~ 1999~ 1199~ 1919~ ~0123456789
Finally, we (1) replace each "marked" group of 9s with 0s, (2) remove the ~s, and (3) remove the character set at the end.
s/9(?=9*~)(?=.*(0))|~| ~0123456789$/$1/g
For example, our example becomes:
1 2 3 4 8 9 10 11 20 100 110 200 910 1000 1100 1910
PHP Example
$str = '0 1 2 3 7 8 9 10 19 99 109 199 909 999 1099 1909';
echo $str . '<br/>';
$str = preg_replace('/$/', ' ~0123456789', $str);
echo $str . '<br/>';
$str = preg_replace('/(?=\d)(?:([0-8])(?=.*\1(\d)\d*$)|(?=.*(1)))(?:(9+)(?=.*(~))|)(?!\d)/', '$2$3$4$5', $str);
echo $str . '<br/>';
$str = preg_replace('/9(?=9*~)(?=.*(0))|~| ~0123456789$/', '$1', $str);
echo $str . '<br/>';
Output:
0 1 2 3 7 8 9 10 19 99 109 199 909 999 1099 1909
0 1 2 3 7 8 9 10 19 99 109 199 909 999 1099 1909 ~0123456789
1 2 3 4 8 9 19~ 11 29~ 199~ 119~ 299~ 919~ 1999~ 1199~ 1919~ ~0123456789
1 2 3 4 8 9 10 11 20 100 110 200 910 1000 1100 1910
Is it possible in a single substitution?
No.
If not, is it at least possible in a single substitution given an upper bound, e.g. numbers up to 9999?
No.
You can't even replace the numbers between 0 and 8 with their respective successor. Once you have matched, and grouped this number:
/([0-8])/
you need to replace it. However, regex doesn't operate on numbers, but on strings. So you can replace the "number" (or better: digit) with twice this digit, but the regex engine does not know it is duplicating a string that holds a numerical value.
Even if you'd do something (silly) as this:
/(0)|(1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)/
so that the regex engine "knows" that if group 1 is matched, the digit '0' is matched, it still cannot do a replacement. You can't instruct the regex engine to replace group 1 with the digit '1', group '2' with the digit '2', etc. Sure, some tools like PHP will let you define a couple of different patterns with corresponding replacement strings, but I get the impression that is not what you were thinking about.
It is not possible by regular expression search and substitution alone.
You have to use use something else to help achieve that. You have to use the programming language at hand to increment the number.
Edit:
The regular expressions definition, as part of Single Unix Specification doesn't mention regular expressions supporting evaluation of aritmethic expressions or capabilities for performing aritmethic operations.
Nonetheless, I know some flavors ( TextPad, editor for Windows) allows you to use \i as a substitution term which is an incremental counter of how many times has the search string been found, but it doesn't evaluate or parse found strings into a number nor does it allow to add a number to it.
I have found a solution in two steps (Javascript) but it relies on indefinite lookaheads, which some regex engines reject:
const incrementAll = s =>
s.replaceAll(/(.+)/gm, "$1\n101234567890")
.replaceAll(/(?:([0-8]|(?<=\d)9)(?=9*[^\d])(?=.*\n\d*\1(\d)\d*$))|(?<!\d)9(?=9*[^\d])(?=(?:.|\n)*(10))|\n101234567890$/gm, "$2$3");
The key thing is to add a list of numbers in order at the end of the string in the first step, and in the second, to find the location relevant digit and capture the digit to its right via a lookahead. There are two other branches in the second step, one for dealing with initial nines, and the other for removing the number sequence.
Edit: I just tested it in safari and it throws an error, but it definately works in firefox.
I needed to increment indices of output files by one from a pipeline I can't modify. After some searches I got a hit on this page. While the readings are meaningful, they really don't give a readable solution to the problem. Yes it is possible to do it with only regex; no it is not as comprehensible.
Here I would like to give a readable solution using Python, so that others don't need to reinvent the wheels. I can imagine many of you may have ended up with a similar solution.
The idea is to partition file name into three groups, and format your match string so that the incremented index is the middle group. Then it is possible to only increment the middle group, after which we piece the three groups together again.
import re
import sys
import argparse
from os import listdir
from os.path import isfile, join
def main():
parser = argparse.ArgumentParser(description='index shift of input')
parser.add_argument('-r', '--regex', type=str,
help='regex match string for the index to be shift')
parser.add_argument('-i', '--indir', type=str,
help='input directory')
parser.add_argument('-o', '--outdir', type=str,
help='output directory')
args = parser.parse_args()
# parse input regex string
regex_str = args.regex
regex = re.compile(regex_str)
# target directories
indir = args.indir
outdir = args.outdir
try:
for input_fname in listdir(indir):
input_fpath = join(indir, input_fname)
if not isfile(input_fpath): # not a file
continue
matched = regex.match(input_fname)
if matched is None: # not our target file
continue
# middle group is the index and we increment it
index = int(matched.group(2)) + 1
# reconstruct output
output_fname = '{prev}{index}{after}'.format(**{
'prev' : matched.group(1),
'index' : str(index),
'after' : matched.group(3)
})
output_fpath = join(outdir, output_fname)
# write the command required to stdout
print('mv {i} {o}'.format(i=input_fpath, o=output_fpath))
except BrokenPipeError:
pass
if __name__ == '__main__': main()
I have this script named index_shift.py. To give an example of the usage, my files are named k0_run0.csv, for bootstrap runs of machine learning models using parameter k. The parameter k starts from zero, and the desired index map starts at one. First we prepare input and output directories to avoid overriding files
$ ls -1 test_in/ | head -n 5
k0_run0.csv
k0_run10.csv
k0_run11.csv
k0_run12.csv
k0_run13.csv
$ ls -1 test_out/
To see how the script works, just print its output:
$ python3 -u index_shift.py -r '(^k)(\d+?)(_run.+)' -i test_in -o test_out | head -n5
mv test_in/k6_run26.csv test_out/k7_run26.csv
mv test_in/k25_run11.csv test_out/k26_run11.csv
mv test_in/k7_run14.csv test_out/k8_run14.csv
mv test_in/k4_run25.csv test_out/k5_run25.csv
mv test_in/k1_run28.csv test_out/k2_run28.csv
It generates bash mv command to rename the files. Now we pipe the lines directly into bash.
$ python3 -u index_shift.py -r '(^k)(\d+?)(_run.+)' -i test_in -o test_out | bash
Checking the output, we have successfully shifted the index by one.
$ ls test_out/k0_run0.csv
ls: cannot access 'test_out/k0_run0.csv': No such file or directory
$ ls test_out/k1_run0.csv
test_out/k1_run0.csv
You can also use cp instead of mv. My files are kinda big, so I wanted to avoid duplicating them. You can also refactor how many you shift as input argument. I didn't bother, cause shift by one is most of my use cases.

How to add a new line in the middle of string in c#

I have the following string which contains reviews for products, I want to move sentences like 1 of 3 and 4 of 13 into new lines
Input string
The mapping features have a lot of inaccuracie 1 of 3
am a little disappointed in the new 4S. 4 of 13
Output string
The mapping features have a lot of inaccuracies
1 of 3
am a little disappointed in the new 4S
4 of 13
I was trying Regex.Replace because it changes all occurrences in the string
I located the string using #"\d+ of \d+"
but how can I keep the variable number in the replacement text? Or can you suggest a different method?
You need to capture the match in order to be able to use it for replacement:
#"(\d+ of \d+)"
To replace this value, use $1.
Something like:
Regex.Replace(input,
#"(\d+ of \d+)",
string.Format(#"{0}$1{0}", Environment.NewLine))

Regex expression to back reference more than 9 values in a replace

I have a regex expression that traverses a string and pulls out 40 values, it looks sort if like the query below, but much larger and more complicated
est(.*)/test>test>(.*)<test><test>(.*)test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test><test>(.*)/test>
My question is how do I use these expressions with the replace command when the number exceeds 9. It seems as if whenever I use \10 it returns the value for \1 and then appends a 0 to the end.
Any help would be much appreciated thanks :)
Also I am using UEStudio, but if a different program does it better then no biggie :)
As pointed out by psycho brm:
Use $10 instead of \10
I am using notepad++ and it works beautifull.
Most of the simple Regex engines used by editors aren't equipped to handle more than 10 matching groups; it doesn't seem like UltraEdit can. I just tried Notepad++ and it won't even match a regex with 10 groups.
Your best bet, I think, is to write something fast in a quick language with a decent regex parser. but that wouldn't answer the question as asked
Here's something in Python:
import re
pattern = re.compile('(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)')
with open('input.txt', 'r') as f:
for line in f:
m = pattern.match(line)
print m.groups()
Note that Python allows backreferences such as \20: in order to have a backreference to group 2 followed by a literal 0, you need to use \g<2>0, which is unambiguous.
Edit:
Most flavors of regex, and editors which include a regex engine, should follow the replace syntax as follows:
abcdefghijklmnop
search: (.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(?<name>.)(.)
note: 1 2 3 4 5 6 7 8 9 10 11 12 13
value: a b c d e f g h i j k l m
replace result:
\11 k1 i.e.: match 1, then the character "1"
${12} l most should support this
${name} l few support named references, but use them where you can.
Named references are usually only possible in very specific flavor of regex libraries, test your tool to know for sure.
put a $ in front of the double digit subgroup: e.g. \1\2\3\4\5\6\7\8\9$10 It worked for me.
Try using named groups; so instead of the tenth:
(.*)
use:
(?<group10>.*)
and then use the following replace string:
${group10}
(That's of course in the absence of a better solution using looping, and remember that there might be different regex syntax flavours depending on your environment.)
If you cannot handle more than 9 subgroups why not initially match groups of 9 and then loop and apply regexes to those matches?
i.e. first match (<test.*/test>)+ and then for each subgroup match on <test(.*)/test>.