I try to catch somes "blocks" of my text file that are endend by a pattern with several "=" symbols.
I want to catch all these block without the final pattern but it's made with "=" that is use on some capture group of my block ... So when i select them, the pattern is always in the last match ...
Do you no a method for exclude it ?
A extract of my regex :
(\d{2}-\d{2}-\d{4} \d{2}:\d{2}) (.*)(Statut)([,:. aA-zZ0-9À-ÖØ-öø-ÿ=><\n\r]*)\n
And block to analyse :
01-10-2021 16:02 utilisateur1Statut A réaliser =>
Ouverte
01-10-2021 16:03 utilisateur1Statut MyFile.txt
01-10-2021 16:04 utilisateur1Statut
utilisateur1 => utilisateur2
======================================================================
Warning : my block can be with one or more row with carriage return ...
Links to regex101 sample : https://regex101.com/r/hXu3QO/1
The last part of the pattern contains a character class [,:. aA-zZ0-9À-ÖØ-öø-ÿ=><\n\r] that also matches = and newlines, so there is no rule to stop matching.
Note that aA-zZ is not the same as [a-zA-Z]
You can exclude the newlines from the character class, and repeat the matching starting with a newline and all lines that do not start with for example === or \d{2}-
You can make the rule as specific as you want of course.
(\d{2}-\d{2}-\d{4} \d{2}:\d{2}) (.*?)(Statut)\s*([,:. a-zA-Z0-9À-ÖØ-öø-ÿ=><]*(?:\n(?!===|\d{2}-)[,:. a-zA-Z0-9À-ÖØ-öø-ÿ=><]+)*)
Regex demo
Related
I'm using the following regular expression pattern:
.*(?<line>^\s*Extends\s+#(?<extends>[_A-Za-z0-9]+)\s*$)?.*
And the following text:
Name #asdf
Extends #extendedClass
Origin #id
What I don't understand is that both of the caught group results (line and extends) are empty, but when I remove the last question mark from the expression the groups are caught.
The line group must be optional since the Extends line is not always present.
I created a fiddle using this expression, which can be accessed at https://regexr.com/4rekk
EDIT
I forgot to mention that I'm using the multiline and dotall flags along with the expression.
It's already been mentioned that the leading .* is capturing everything when you make your (?<line>) group optional. The following is not directly related to your question but it may be useful information (if not, just ignore):
You need to be careful elsewhere. You are using ^ and $ to match the start and end of lines as well as the start and end of the string. But the $ character will not consume the newline character that marks the end of a line. So:
'Line 1\nLine 2'.match(/^Line 1$^Line 2/m) returns null
while
'Line 1\nLine 2'.match(/^Line 1\n^Line 2/m) returns a match
So in your case if you were trying to capture all three lines, any of which were optional, you would write the regex for one of the lines as follows to make sure you consume the newline:
/(?<line>^\s*Extends\s+#(?<extends>[_A-Za-z0-9]+)[^\S\n]*\n)?/ms
Where you had specified \s*$, I have [^\S\n]*\n. [^\S\n]* is a double negative that says one or more non non-white space character excluding the newline character. So it will consume all white space characters except the newline character. If you wanted to look for any of the three lines in your example (any or all are optional), then the following code snippet should do it. I have used the RegExp function to create the regex so that it can be split across multiple lines. Unfortunately, it takes a string as its argument and so some backslash characters have to be doubled up:
let s = ` Name #asdf
Extends #extendedClass
Origin #id
`;
let regex = new RegExp(
"(?<line0>^\\s*Name\\s+#(?<name>[_A-Za-z0-9]+)[^\\S\\n]*\\n)?" +
"(?<line>^\\s*Extends\\s+#(?<extends>[_A-Za-z0-9]+)[^\\S\\n]*\\n)?" +
"(?<line2>^\\s*Origin\\s+#(?<id>[_A-Za-z0-9]+)[^\\S\\n]*\\n)?",
'm'
);
let m = s.match(regex);
console.log(m.groups);
The above code snippet seems to have a problem under Firefox (an invalid regex flag, 's', is flagged on a line that doesn't exist in the above snippet). See the following regex demo.
And without named capture groups:
let s = ` Name #asdf
Extends #extendedClass
Origin #id
`;
let regex = new RegExp(
"(^\\s*Name\\s+#([_A-Za-z0-9]+)[^\\S\\n]*\\n)?" +
"(^\\s*Extends\\s+#([_A-Za-z0-9]+)[^\\S\\n]*\\n)?" +
"(^\\s*Origin\\s+#([_A-Za-z0-9]+)[^\\S\\n]*\\n)?",
'm'
);
let m = s.match(regex);
console.log(m);
Learning regex in bash, i am trying to fetch all lines which ends with .com
Initially i did :
cat patternNpara.txt | egrep "^[[:alnum:]]+(.com)$"
why : +matches one or more occurrences, so placing it after alnum should fetch the occurrence of any digit,word or signs but apparently, this logic is failing....
Then i did this : (purely hit-and-try, not applying any logic really...) and it worked
cat patternNpara.txt | egrep "^[[:alnum:]].+(.com)$"
whats confusing me : . matches only single occurrence, then, how am i getting the output...i mean how is it really matching the pattern???
Question : whats the difference between [[:alnum:]]+ and [[:alnum:]].+ (this one has . in it) in the above matching pattern and how its working???
PS : i am looking for a possible explanation...not, try it this way thing... :)
Some test lines for the file patternNpara.txt which are fetched as output!
valid email = abc#abc.com
invalid email = ab#abccom
another invalid = abc#.com
1 : abc,s,11#gmail.com
2: abc.s.11#gmail.com
Looking at your screenshot it seems you're trying to match email address that has # character also which is not included in your regex. You can use this regex:
egrep "[#[:alnum:]]+(\.com)" patternNpara.txt
DIfference between 2 regex:
[[:alnum:]] matches only [a-zA-Z0-9]. If you have # or , then you need to include them in character class as well.
Your 2nd case is including .+ pattern which means 1 or more matches of ANY CHARACTER
If you want to match any lines that end with '.com', you should use
egrep ".*\.com$" file.txt
To match all the following lines
valid email = abc#abc.com
invalid email = ab#abccom
another invalid = abc#.com
1 : abc,s,11#gmail.com
2: abc.s.11#gmail.com
^[[:alnum:]].+(.com)$ will work, but ^[[:alnum:]]+(.com)$ will not. Here is the reasons:
^[[:alnum:]].+(.com)$ means to match strings that start with a a-zA-Z or 0-9, flows two or more any characters, and end with a 'com' (not '.com').
^[[:alnum:]]+(.com)$ means to match strings that start with one or more a-zA-Z or 0-9, flows one character that could be anything, and end with a 'com' (not '.com').
Try this (with "positive-lookahead") :
.+(?=\.com)
Demo :
http://regexr.com?38bo0
I'm trying to create a regex which will validate input to match any character but it should exclude input which is surrounded with prefixes (i.e. {#..#}, {#..#} or {$..$}).
Given is the example:
Free text which is fine // should return true
{#some other text#} // should return false
Text with numbers 671 // should return true
{#Hello world#} // should return false
{$Hello Mars$} // should return false
{$some text which i do not close // should return true
This should be possible with the use of negative look-around, something along the lines of:
^(\?!=(\{#[^.]+#\}))(\?!=(\{$[^.]+$\}))(\?!=(\{#[^.]+#\})).*
Any help would be greatly appreciated : )
You syntax is a bit weird if you ask me... I would suggest:
^(?!\{(?:[$##]).*(?:[$##])\}).*
Demo
First 'group' is \{(?:[$##]) which looks for the opening prefix, then .* to match everything in the middle and (?:[$##])\} to match the closing suffix.
Note that it will not allow things like:
{$Hello Mars$} how are you?
If you want it to accept this as well, add an end of line anchor:
^(?!\{(?:[$##]).*(?:[$##])\}$).*
^
Demo
You can use the character class to have the different symbols [#$#] and is shorter than having multiple negative lookarounds or | operators in side :)
EDIT: To prevent things like {#Free text which is fine$}' you could use:
^(?!\{([$##]).*\1\}).*
Or
^(?!\{([$##]).*\1\}$).*
For the second version.
\1 is a backreference and refers to the first captured group (any of $, #, or #).
I want to create a rule to remove array( and ) from this text:
"price"=> array(129),
to get:
"price"=> 129,
I tried this expression without success:
(?<="price"=>\s*)array\((?=\d*)\)(?=,)
Then I decided to made replacement in 2 steps. Firstly, I removed array(:
(?<="price"=>\s\s\s\s\s)array\(
And got:
"price"=> 129),
So I had to remove only a closing parenthesis ). I tried without success:
(?<="price"=>\s*\d*)\)(?=,)
This works, but only for a known number of whitespaces and digits:
(?<="price"=>\s\s\s\s\s\d\d\d)\)(?=,)
Try this for the find:
("price"=>\s+)array\((\d+)\)
and this for the replace:
\1\2
you can match whole line with this
\"price"[^a)]+(array\()\d+(\),)
it contains one group for "array(" and another for "),"
Try this:
(?:(?<=\"price\"=>\s*)array\((?=\d+\)))|(?<=\"price\"=>\s*array\(\d+)\)
The regex consists mainly two parts (the pipe in the middle is an alternation symbol which means if the first part doesn't match it should look for the second part).
The first part checks if array( is preceded by "price"=> ... and is succeded by ) by using the look-behind (?<= ... ) and look-ahead (?= ... ) symbol respectively.
(?:(?<=\"price\"=>\s*)array\((?=\d+\)))
Then we have a pipe (explained above)..
|
The second part checks if ) is preceded by everything we've matched before ("price"=> array(129) also using the look-behind symbol (<= ... ):
(?<=\"price\"=>\s*array\(\d+)\)
Thus for the string "price"=> array(129), the result should be two matches: array( and ).
Please let me know if this works for you.
I'm trying to learn something about regular expressions.
Here is what I'm going to match:
/parent/child
/parent/child?
/parent/child?firstparam=abc123
/parent/child?secondparam=def456
/parent/child?firstparam=abc123&secondparam=def456
/parent/child?secondparam=def456&firstparam=abc123
/parent/child?thirdparam=ghi789&secondparam=def456&firstparam=abc123
/parent/child?secondparam=def456&firstparam=abc123&thirdparam=ghi789
/parent/child?thirdparam=ghi789
/parent/child/
/parent/child/?
/parent/child/?firstparam=abc123
/parent/child/?secondparam=def456
/parent/child/?firstparam=abc123&secondparam=def456
/parent/child/?secondparam=def456&firstparam=abc123
/parent/child/?thirdparam=ghi789&secondparam=def456&firstparam=abc123
/parent/child/?secondparam=def456&firstparam=abc123&thirdparam=ghi789
/parent/child/?thirdparam=ghi789
My expression should "grabs" abc123 and def456.
And now just an example about what I'm not going to match ("question mark" is missing):
/parent/child/firstparam=abc123&secondparam=def456
Well, I built the following expression:
^(?:/parent/child){1}(?:^(?:/\?|\?)+(?:firstparam=([^&]*)|secondparam=([^&]*)|[^&]*)?)?
But that doesn't work.
Could you help me to understand what I'm doing wrong?
Thanks in advance.
UPDATE 1
Ok, I made other tests.
I'm trying to fix the previous version with something like this:
/parent/child(?:(?:\?|/\?)+(?:firstparam=([^&]*)|secondparam=([^&]*)|[^&]*)?)?$
Let me explain my idea:
Must start with /parent/child:
/parent/child
Following group is optional
(?: ... )?
The previous optional group must starts with ? or /?
(?:\?|/\?)+
Optional parameters (I grab values if specified parameters are part of querystring)
(?:firstparam=([^&]*)|secondparam=([^&]*)|[^&]*)?
End of line
$
Any advice?
UPDATE 2
My solution must be based just on regular expressions.
Just for example, I previously wrote the following one:
/parent/child(?:[?&/]*(?:firstparam=([^&]*)|secondparam=([^&]*)|[^&]*))*$
And that works pretty nice.
But it matches the following input too:
/parent/child/firstparam=abc123&secondparam=def456
How could I modify the expression in order to not match the previous string?
You didn't specify a language so I'll just usre Perl. So basically instead of matching everything, I just matched exactly what I thought you needed. Correct me if I am wrong please.
while ($subject =~ m/(?<==)\w+?(?=&|\W|$)/g) {
# matched text = $&
}
(?<= # Assert that the regex below can be matched, with the match ending at this position (positive lookbehind)
= # Match the character “=” literally
)
\\w # Match a single character that is a “word character” (letters, digits, and underscores)
+? # Between one and unlimited times, as few times as possible, expanding as needed (lazy)
(?= # Assert that the regex below can be matched, starting at this position (positive lookahead)
# Match either the regular expression below (attempting the next alternative only if this one fails)
& # Match the character “&” literally
| # Or match regular expression number 2 below (attempting the next alternative only if this one fails)
\\W # Match a single character that is a “non-word character”
| # Or match regular expression number 3 below (the entire group fails if this one fails to match)
\$ # Assert position at the end of the string (or before the line break at the end of the string, if any)
)
Output:
This regex will work as long as you know what your parameter names are going to be and you're sure that they won't change.
\/parent\/child\/?\?(?:(?:firstparam|secondparam|thirdparam)\=([\w]+)&?)(?:(?:firstparam|secondparam|thirdparam)\=([\w]+)&?)?(?:(?:firstparam|secondparam|thirdparam)\=([\w]+)&?)?
Whilst regex is not the best solution for this (the above code examples will be far more efficient, as string functions are way faster than regexes) this will work if you need a regex solution with up to 3 parameters. Out of interest, why must the solution use only regex?
In any case, this regex will match the following strings:
/parent/child?firstparam=abc123
/parent/child?secondparam=def456
/parent/child?firstparam=abc123&secondparam=def456
/parent/child?secondparam=def456&firstparam=abc123
/parent/child?thirdparam=ghi789&secondparam=def456&firstparam=abc123
/parent/child?secondparam=def456&firstparam=abc123&thirdparam=ghi789
/parent/child?thirdparam=ghi789
/parent/child/?firstparam=abc123
/parent/child/?secondparam=def456
/parent/child/?firstparam=abc123&secondparam=def456
/parent/child/?secondparam=def456&firstparam=abc123
/parent/child/?thirdparam=ghi789&secondparam=def456&firstparam=abc123
/parent/child/?secondparam=def456&firstparam=abc123&thirdparam=ghi789
/parent/child/?thirdparam=ghi789
It will now only match those containing query string parameters, and put them into capture groups for you.
What language are you using to process your matches?
If you are using preg_match with PHP, you can get the whole match as well as capture groups in an array with
preg_match($regex, $string, $matches);
Then you can access the whole match with $matches[0] and the rest with $matches[1], $matches[2], etc.
If you want to add additional parameters you'll also need to add them in the regex too, and add additional parts to get your data. For example, if you had
/parent/child/?secondparam=def456&firstparam=abc123&fourthparam=jkl01112&thirdparam=ghi789
The regex will become
\/parent\/child\/?\?(?:(?:firstparam|secondparam|thirdparam|fourthparam)\=([\w]+)&?)(?:(?:firstparam|secondparam|thirdparam|fourthparam)\=([\w]+)&?)?(?:(?:firstparam|secondparam|thirdparam|fourthparam)\=([\w]+)&?)?(?:(?:firstparam|secondparam|thirdparam|fourthparam)\=([\w]+)&?)?
This will become a bit more tedious to maintain as you add more parameters, though.
You can optionally include ^ $ at the start and end if the multi-line flag is enabled. If you also need to match the whole lines without query strings, wrap this whole regex in a non-capture group (including ^ $) and add
|(?:^\/parent\/child\/?\??$)
to the end.
You're not escaping the /s in your regex for starters and using {1} for a single repetition of something is unnecessary; you only use those when you want more than one repetition or a range of repetitions.
And part of what you're trying to do is simply not a good use of a regex. I'll show you an easier way to deal with that: you want to use something like split and put the information into a hash that you can check the contents of later. Because you didn't specify a language, I'm just going to use Perl for my example, but every language I know with regexes also has easy access to hashes and something like split, so this should be easy enough to port:
# I picked an example to show how this works.
my $route = '/parent/child/?first=123&second=345&third=678';
my %params; # I'm going to put those URL parameters in this hash.
# Perl has a way to let me avoid escaping the /s, but I wanted an example that
# works in other languages too.
if ($route =~ m/\/parent\/child\/\?(.*)/) { # Use the regex for this part
print "Matched route.\n";
# But NOT for this part.
my $query = $1; # $1 is a Perl thing. It contains what (.*) matched above.
my #items = split '&', $query; # Each item is something like param=123
foreach my $item (#items) {
my ($param, $value) = split '=', $item;
$params{$param} = $value; # Put the parameters in a hash for easy access.
print "$param set to $value \n";
}
}
# Now you can check the parameter values and do whatever you need to with them.
# And you can add new parameters whenever you want, etc.
if ($params{'first'} eq '123') {
# Do whatever
}
My solution:
/(?:\w+/)*(?:(?:\w+)?\?(?:\w+=\w+(?:&\w+=\w+)*)?|\w+|)
Explain:
/(?:\w+/)* match /parent/child/ or /parent/
(?:\w+)?\?(?:\w+=\w+(?:&\w+=\w+)*)? match child?firstparam=abc123 or ?firstparam=abc123 or ?
\w+ match text like child
..|) match nothing(empty)
If you need only query string, pattern would reduce such as:
/(?:\w+/)*(?:\w+)?\?(\w+=\w+(?:&\w+=\w+)*)
If you want to get every parameter from query string, this is a Ruby sample:
re = /\/(?:\w+\/)*(?:\w+)?\?(\w+=\w+(?:&\w+=\w+)*)/
s = '/parent/child?secondparam=def456&firstparam=abc123&thirdparam=ghi789'
if m = s.match(re)
query_str = m[1] # now, you can 100% trust this string
query_str.scan(/(\w+)=(\w+)/) do |param,value| #grab parameter
printf("%s, %s\n", param, value)
end
end
output
secondparam, def456
firstparam, abc123
thirdparam, ghi789
This script will help you.
First, i check, is there any symbol like ?.
Then, i kill first part of line (left from ?).
Next, i split line by &, where each value splitted by =.
my $r = q"/parent/child
/parent/child?
/parent/child?firstparam=abc123
/parent/child?secondparam=def456
/parent/child?firstparam=abc123&secondparam=def456
/parent/child?secondparam=def456&firstparam=abc123
/parent/child?thirdparam=ghi789&secondparam=def456&firstparam=abc123
/parent/child?secondparam=def456&firstparam=abc123&thirdparam=ghi789
/parent/child?thirdparam=ghi789
/parent/child/
/parent/child/?
/parent/child/?firstparam=abc123
/parent/child/?secondparam=def456
/parent/child/?firstparam=abc123&secondparam=def456
/parent/child/?secondparam=def456&firstparam=abc123
/parent/child/?thirdparam=ghi789&secondparam=def456&firstparam=abc123
/parent/child/?secondparam=def456&firstparam=abc123&thirdparam=ghi789
/parent/child/?thirdparam=ghi789";
for my $string(split /\n/, $r){
if (index($string,'?')!=-1){
substr($string, 0, index($string,'?')+1,"");
#say "string = ".$string;
if (index($string,'=')!=-1){
my #params = map{$_ = [split /=/, $_];}split/\&/, $string;
$"="\n";
say "$_->[0] === $_->[1]" for (#params);
say "######next########";
}
else{
#print "there is no params!"
}
}
else{
#say "there is no params!";
}
}