decision on regular expression length - regex

I want to accomplish the following requirements using Regex only (no C# code can be used )
• BTN length is 12 and BTN starts with 0[123456789] then it should remove one digit from left and one digit from right.
WORKING CORRECTLY
• BTN length is 12 and it’s not the case stated above then it should always return 10 right digits by removing 2 from the start. (e.g. 491234567891 should be changed to 1234567891)
NOT WORKING CORRECTLY
• BTN length is 11 and it should remove one digit from left. WORKING CORRECTLY
for length <=10 BTNs , nothing is required to be done , they would remain as it is or Regex may get failed too on them , thats acceptable .
USING SQL this can be achieved like this
case when len(BTN) = 12 and BTN like '0[123456789]%' then SUBSTRING(BTN,2,10) else RIGHT(BTN,10) end
but how to do this using Regex .
So far I have used and able to get some result correct using this regex
[0*|\d\d]*(.{10}) but by this regex I am not able to correctly remove 1st and last character of a BTN like this 015732888810 to 1573288881 as this regex returns me this 5732888810 which is wrong
code is
string s = "111112573288881,0573288881000,057328888105,005732888810,15732888815,344956345335,004171511326,01777203102,1772576210,015732888810,494956345335";
string[] arr = s.Split(',');
foreach (string ss in arr)
{
// Match mm = Regex.Match(ss, #"\b(?:00(\d{10})|0(\d{10})\d?|(\d{10}))\b");
// Match mm = Regex.Match(ss, "0*(.{10})");
// ([0*|\\d\\d]*(.{10}))|
Match mm = Regex.Match(ss, "[0*|\\d\\d]*(.{10})");
// Match mm = Regex.Match(ss, "(?(^\\d{12}$)(.^{12}$)|(.^{10}$))");
// Match mm = Regex.Match(ss, "(info)[0*|\\d\\d]*(.{10}) (?(1)[0*|\\d\\d]*(.{10})|[0*|\\d\\d]*(.{10}))");
string m = mm.Groups[1].Value;
Console.WriteLine("Original BTN :"+ ss + "\t\tModified::" + m);
}

This should work:
(0(\d{10})0|\d\d(\d{10}))
UPDATE:
(0(\d{10})0|\d{1,2}(\d{10}))
1st alternate will match 12-digits with 0 on left and 0 on right and give you only 10 in between.
2nd alternate will match 11 or 12 digits and give you the right 10.
EDIT:
The regex matches the spec, but your code doesn't read the results correctly. Try this:
Match mm = Regex.Match(ss, "(0(\\d{10})0|\\d{1,2}(\\d{10}))");
string m = mm.Groups[2].Value;
if (string.IsNullOrEmpty(m))
m = mm.Groups[3].Value;
Groups are as follows:
index 0: returns full string
index 1: returns everything inside the outer closure
index 2: returns only what matches in the closure inside the first alternate
index 3: returns only what matches in the closure inside the second alternate
NOTE: This does not deal with anything greater than 12 digits or less than 11. Those entries will either fail or return 10 digits from somewhere. If you want results for those use this:
"(0(\\d{10})0|\\d*(\\d{10}))"
You'll get rightmost 10 digits for more than 12 digits, 10 digits for 10 digits, nothing for less than 10 digits.
EDIT:
This one should cover your additional requirements from the comments:
"^(?:0|\\d*)(\\d{10})0?$"
The (?:) makes a grouping excluded from the Groups returned.
EDIT:
This one might work:
"^(?:0?|\\d*)(\\d{10})\\d?$"

(?(^\d{12}$)(?(^0[1-9])0?(?<digit>.{10})|\d*(?<digit>.{10}))|\d*(?<digit>.{10}))
which does the exact same thing as sql query + giving result in Group[1] all the time so i didn't had to change the code a bit :)

Related

RegEx vscode - replace decimal places and round correctly

Is it possible to use regex to round decimal places?
I have lines that look like this but without any spaces (space added for readability).
0, 162.3707542, -162.3707542
128.2, 151.8299471, -23.62994709 // this 151.829 should lead to 151.83
I want to remove all numbers after the second decimal position and if possible round the second decimal position based on the third position.
0, 162.37, -162.37
128.2, 151.82, -23.62 // already working .82
..., 151.83, ... // intended .83 <- this is my question
What is working
The following regex (see this sample on regex101.com) almost does what i want
([0-9]+\.)([0-9]{2})(\d{0,}) // search
$1$2 // replace
My understanding
The search works like this
group: ([0-9]+\.) find 1 up to n numbers and a point
group: ([0-9]{2}) followd by 2 numbers
group: (\d{0,}) followed by 0 or more numbers / digits
In visual-studio-code in the replacement field only group 1 and 2 are referenced $1$2.
This results in this substitution (regex101.com)
Question
Is it possible to change the last digit of $2 (group two) based on the first digit in $3 (group three) ?
My intention is to round correctly. In the sample above this would mean
151.8299471 // source
151.82 // current result
151.83 // desired result 2 was changed to 3 because of third digit 9
It is not only that you need to update the digit of $2. if the number is 199.995 you have to modify all digits of your result.
You can use the extension Regex Text Generator.
You can use a predefined set of regex's.
"regexTextGen.predefined": {
"round numbers": {
"originalTextRegex": "(-?\\d+\\.\\d+)",
"generatorRegex": "{{=N[1]:fixed(2):simplify}}"
}
}
With the same regex (-?\\d+\\.\\d+) in the VSC Find dialog select all number you want, you can use Find in Selection and Alt+Enter.
Then execute the command: Generate text based on Regular Expression.
Select the predefined option and press Enter a few times. You get a preview of the result, you can escape the UI and get back the original text.
In the process you can edit generatorRegex to change the number of decimals or to remove the simplify.
It was easier than I thought, once I found the Number.toFixed(2) method.
Using this extension I wrote, Find and Transform, make this keybinding in your keybindings.json:
{
"key": "alt+r", // whatever keybinding you want
"command": "findInCurrentFile",
"args": {
"find": "(-?[0-9]+\\.\\d{3,})", // only need the whole number as one capture group
"replace": [
"$${", // starting wrapper to indicate a js operation begins
"return $1.toFixed(2);", // $1 from the find regex
"}$$" // ending wrapper to indicate a js operation ends
],
// or simply in one line
// "replace": "$${ return $1.toFixed(2); }$$",
"isRegex": true
},
}
[The empty lines above are there just for readability.]
This could also be put into a setting, see the README, so that a command appears in the Command Palette with the title of your choice.
Also note that javascript rounds -23.62994709 to -23.63. You had -23.62 in your question, I assume -23.63 is correct.
If you do want to truncate things like 4.00 to 4 or 4.20 to 4.2 use this replace instead.
"replace": [
"$${",
"let result = $1.toFixed(2);",
"result = String(result).replace(/0+$/m, '').replace(/\\.$/m, '');",
"return result;",
"}$$"
],
We are able to round-off decimal numbers correctly using regular expressions.
We need basically this regex:
secondDD_regx = /(?<=[\d]*\.[\d]{1})[\d]/g; // roun-off digit
thirdDD_regx = /(?<=[\d]*\.[\d]{2})[\d]/g; // first discard digit
isNonZeroAfterThirdDD_regx = /(?<=[\d]*\.[\d]{3,})[1-9]/g;
isOddSecondDD_regx = /[13579]/g;
Full code (round-off digit up to two decimal places):
const uptoOneDecimalPlaces_regx = /[\+\-\d]*\.[\d]{1}/g;
const secondDD_regx = /(?<=[\d]*\.[\d]{1})[\d]/g;
const thirdDD_regx = /(?<=[\d]*\.[\d]{2})[\d]/g;
const isNonZeroAfterThirdDD_regx = /(?<=[\d]*\.[\d]{3,})[1-9]/g;
const num = '5.285';
const uptoOneDecimalPlaces = num.match(uptoOneDecimalPlaces_regx)?.[0];
const secondDD = num.match(secondDD_regx)?.[0];
const thirdDD = num.match(thirdDD_regx)?.[0];
const isNonZeroAfterThirdDD = num.match(isNonZeroAfterThirdDD_regx)?.[0];
const isOddSecondDD = /[13579]/g.test(secondDD);
// check carry
const carry = !thirdDD ? 0 : thirdDD > 5 ? 1 : thirdDD < 5 ? 0 : isNonZeroAfterThirdDD ? 1 : isOddSecondDD ? 1 : 0;
let roundOffValue;
if(/9/g.test(secondDD) && carry) {
roundOffValue = (Number(`${uptoOneDecimalPlaces}` + `${secondDD ? Number(secondDD) : 0}`) + Number(`0.0${carry}`)).toString();
} else {
roundOffValue = (uptoOneDecimalPlaces + ((secondDD ? Number(secondDD) : 0) + carry)).toString();
}
// Beaufity output : show exactly 2 decimal places if output is x.y or x
const dd = roundOffValue.match(/(?<=[\d]*[\.])[\d]*/g)?.toString().length;
roundOffValue = roundOffValue + (dd=== undefined ? '.00' : dd === 1 ? '0' : '');
console.log(roundOffValue);
For more details check: Round-Off Decimal Number properly using Regular Expression🤔

How to find any non-digit characters using RegEx in ABAP

I need a Regular Expression to check whether a value contains any other characters than digits between 0 and 9.
I also want to check the length of the value.
The RegEx I´ve made: ^([0-9]\d{6})$
My test value is: 123Z45 and 123456
The ABAP code:
FIND ALL OCCURENCES OF REGEX '^([0-9]\d{6})$' IN L_VALUE RESULTS DATA(LT_RESULTS).
I´m expecting a result in LT_RESULTS, when I´m testing the first test value '123Z45', because there is a non-digit character.
But LT_RESULTS is in nearly every test case empty.
Your expression ^([0-9]\d{6})$ translates to:
^ - start of input
( - begin capture group
[0-9] - a character between 0 and 9
\d{6} - six digits (digit = character between 0 and 9)
) - end capture group
$ - end of input
So it will only match 1234567 (7 digit strings), not 123456, or 123Z45.
If you just need to find a string that contains non digits you could use the following instead: ^\d*[^\d]+\d*$
* - previous element may occur zero, one or more times
[^\d] - ^ right after [ means "NOT", i.e. any character which is not a digit
+ - previous element may occur one or more times
Example:
const expression = /^\d*[^\d]+\d*$/;
const inputs = ['123Z45', '123456', 'abc', 'a21345', '1234f', '142345'];
console.log(inputs.filter(i => expression.test(i)));
You can also use this character class if you want to extract non-digit group:
DATA(l_guid) = '0074162D8EAA549794A4EF38D9553990680B89A1'.
DATA(regx) = '[[:alpha:]]+'.
DATA(substr) = match( val = l_guid
regex = regx
occ = 1 ).
It finds a first occured non-digit group of characters and shows it.
If you want to just check if they are exists or how much of them reside in your string, count built-in function is your friend:
DATA(how_many) = count( val = l_guid regex = regx ).
DATA(yes) = boolc( count( val = l_guid regex = regx ) > 0 ).
Match and count exist since ABAP 7.50.
If you don't need a Regular Expression for something more complex, ABAP has some nice comparison operators CO (Contains Only), CA, NA etc for you. Something like:
IF L_VALUE CO '0123456789' AND STRLEN( L_VALUE ) = 6.

Filter a string using regular expression

I tried the following code. However, the result is not what I want.
$strLine = "100.11 Q9"
$sortString = StringRegExp ($strLine,'([0-9\.]{1,7})', $STR_REGEXPARRAYMATCH)
MsgBox(0, "", $sortString[0],2)
The output shows 100.11, but I want 100.11 9. How could I display it this way using a regular expression?
$sPattern = "([0-9\.]+)\sQ(\d+)"
$strLine = "100.11 Q9"
$sortString = StringRegExpReplace($strLine, $sPattern, '\1 \2')
MsgBox(0, "$sortString", $sortString, 2)
$strLine = "100.11 Q9"
$sortString = StringRegExp($strLine, $sPattern, 3); array of global matches.
For $i1 = 0 To UBound($sortString) -1
MsgBox(0, "$sortString[" & $i1 & "]", $sortString[$i1], 2)
Next
The pattern is to get the 2 groups being 100.11 and 9.
The pattern will 1st match the group with any digit and dot until it reach
/s which will match the space. It will then match the Q. The 2nd group
matches any remaining digits.
StringRegExpReplace replaces the whole string with 1st and 2nd groups
separated with a space.
StringRegExp get the 2 groups as 2 array elements.
Choose 1 from the 2 types regexp above of which you prefer.

Capture number inside tag in Qt

My tag struct looks like this:
<sml8/>
combination of < , sml , digits (one or two) and />
Is there anyway to capture number inside tag?
for example in above I want capture 8 inside
I've defined regular expression and I tried to capture it by digit position but it's not working for me.
QRegExp rxlen("<sml(.*)/>");
int index = rxlen.pos(3);
I guess it's not correct way and it gives me position of digit although I want value of digit (or digits).
You need to use capturedTexts() together with <sml(\\d{1,2})/> regex (it matches <sml literally, then 1 or 2 digits capturing them into Captured group 1, then />:
QString str = "<sml8/>";
QRegExp rxlen("<sml(\\d{1,2})/>");
int pos = rxlen.indexIn(str);
QStringList list = rxlen.capturedTexts();
QString my_number = list[1];

regex with all components optionals, how to avoid empty matches

I have to process a comma separated string which contains triplets of values and translate them to runtime types,the input looks like:
"1x2y3z,80r160g255b,48h30m50s,1x3z,255b,1h,..."
So each substring should be transformed this way:
"1x2y3z" should become Vector3 with x = 1, y = 2, z = 3
"80r160g255b" should become Color with r = 80, g = 160, b = 255
"48h30m50s" should become Time with h = 48, m = 30, s = 50
The problem I'm facing is that all the components are optional (but they preserve order) so the following strings are also valid Vector3, Color and Time values:
"1x3z" Vector3 x = 1, y = 0, z = 3
"255b" Color r = 0, g = 0, b = 255
"1h" Time h = 1, m = 0, s = 0
What I have tried so far?
All components optional
((?:\d+A)?(?:\d+B)?(?:\d+C)?)
The A, B and C are replaced with the correct letter for each case, the expression works almost well but it gives twice the expected results (one match for the string and another match for an empty string just after the first match), for example:
"1h1m1s" two matches [1]: "1h1m1s" [2]: ""
"11x50z" two matches [1]: "11x50z" [2]: ""
"11111h" two matches [1]: "11111h" [2]: ""
This isn't unexpected... after all an empty string matches the expression when ALL of the components are empty; so in order to fix this issue I've tried the following:
1 to 3 quantifier
((?:\d+[ABC]){1,3})
But now, the expression matches strings with wrong ordering or even repeated components!:
"1s1m1h" one match, should not match at all! (wrong order)
"11z50z" one match, should not match at all! (repeated components)
"1r1r1b" one match, should not match at all! (repeated components)
As for my last attempt, I've tried this variant of my first expression:
Match from begin ^ to the end $
^((?:\d+A)?(?:\d+B)?(?:\d+C)?)$
And it works better than the first version but it still matches the empty string plus I should first tokenize the input and then pass each token to the expression in order to assure that the test string could match the begin (^) and end ($) operators.
EDIT: Lookahead attempt (thanks to Casimir et Hippolyte)
After reading and (try to) understanding the regex lookahead concept and with the help of Casimir et Hippolyte answer I've tried the suggested expression:
\b(?=[^,])(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b
Against the following test string:
"48h30m50s,1h,1h1m1s,11111h,1s1m1h,1h1h1h,1s,1m,1443s,adfank,12322134445688,48h"
And the results were amazing! it is able to detect complete valid matches flawlessly (other expressions gave me 3 matches on "1s1m1h" or "1h1h1h" which weren't intended to be matched at all). Unfortunately it captures emtpy matches everytime a unvalid match is found so a "" is detected just before "1s1m1h", "1h1h1h", "adfank" and "12322134445688", so I modified the Lookahead condition to get the expression below:
\b(?=(?:\d+[ABC]){1,3})(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b
It gets rid of the empty matches in any string which doesn't match (?:\d+[ABC]){1,3}) so the empty matches just before "adfank" and "12322134445688" are gone but the ones just before "1s1m1h", "1h1h1h" are stil detected.
So the question is: Is there any regular expression which matches three triplet values in a given order where all component is optional but should be composed of at least one component and doesn't match empty strings?
The regex tool I'm using is the C++11 one.
Yes, you can add a lookahead at the begining to ensure there is at least one character:
^(?=.)((?:\d+A)?(?:\d+B)?(?:\d+C)?)$
If you need to find this kind of substring in a larger string (so without to tokenize before), you can remove the anchors and use a more explicit subpattern in a lookahead:
(?=\d+[ABC])((?:\d+A)?(?:\d+B)?(?:\d+C)?)
In this case, to avoid false positive (since you are looking for very small strings that can be a part of something else), you can add word-boundaries to the pattern:
\b(?=\d+[ABC])((?:\d+A)?(?:\d+B)?(?:\d+C)?)\b
Note: in a comma delimited string: (?=\d+[ABC]) can be replaced by (?=[^,])
I think this might do the trick.
I am keying on either the beginning of the string to match ^ or the comma separator , for fix the start of each match: (?:^|,).
Example:
#include <regex>
#include <iostream>
const std::regex r(R"~((?:^|,)((?:\d+[xrh])?(?:\d+[ygm])?(?:\d+[zbs])?))~");
int main()
{
std::string test = "1x2y3z,80r160g255b,48h30m50s,1x3z,255b";
std::sregex_iterator iter(test.begin(), test.end(), r);
std::sregex_iterator end_iter;
for(; iter != end_iter; ++iter)
std::cout << iter->str(1) << '\n';
}
Output:
1x2y3z
80r160g255b
48h30m50s
1x3z
255b
Is that what you are after?
EDIT:
If you really want to go to town and make empty expressions unmatched then as far as I can tell you have to put in every permutation like this:
const std::string A = "(?:\\d+[xrh])";
const std::string B = "(?:\\d+[ygm])";
const std::string C = "(?:\\d+[zbs])";
const std::regex r("(?:^|,)(" + A + B + C + "|" + A + B + "|" + A + C + "|" + B + C + "|" + A + "|" + B + "|" + C + ")");