Extract GUID from line via regular expression in perl - regex
I have problems testing for and when successful extracting a GUID from a line of a textfile. Given a guid 12345678-1234-1234-1234-123456789abc, where all hexadecimal characters are allowed (I use digits and abc to display the number of characters each substring contains), I tried it like this
my $myline = " set whatevervariable = \'aaaaaaaa-bbbb-cccc-1234-aaaaaaaabbbb\'";
my $guid =($myline =~ m/[A-Fa-f0-9]{8}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{4}[\-]([A-Fa-f0-9]){12}/gi)
The test works fine, but how can I extract the GUID afterwards and use it as a string in my perl script? The [] operator does not work...
Thanks for help,
G.
use group:
my $guid =($myline =~ m/([A-Fa-f0-9]{8}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{4}[\-]([A-Fa-f0-9]){12})/gi)
# note parens here __^ and here __^
You could also simplify the regex:
my $guid =($myline =~ m/([a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-([a-f\d]){12})/gi)
According to daxim's comment, you'd add a modifier:
my $guid =($myline =~ m/([a-f\d]{8}-[a-f\d]{4}-[a-f\d]{4}-[a-f\d]{4}-([a-f\d]){12})/gia)
or use
my $guid =($myline =~ m/([[:xdigit:]]{8}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-[[:xdigit:]]{4}-([[:xdigit:]]){12})/gia)
my $myline = " set whatevervariable = \'aaaaaaaa-bbbb-cccc-1234-aaaaaaaabbbb\'";
my ($guid) =($myline =~ m/([A-Fa-f0-9]{8}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{4}[\-][A-Fa-f0-9]{12})/gi);
There are two changes:
you had your parens wrong.
when you do: $variable = $some_other =~ m/.../g; - the $variable will contain number of matches. If you want the match, you should do:my ( $variable ) = ...`. If there is a change of actually having more than one guid in the file, use: my #guids = $myline =~ m/..../g;
Additionally - since you are using //i flag, there is no need to use [A-Fa-f] - simple [a-f] is good enough. What's more - you don't have to do [-] thing. - character is not magical in regexps.
This all sums to:
my #guids = $myline =~ m/([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})/gi;
Related
Powershell - How to replace a number with a variable in a string?
Trying to replace a number (20 with a variable $cntr=120) in a string using replace operator. But getting stuck with $cntr in the output. Where I am doing wrong? Any better solutions please. Input string myurl.com/search?project=ABC&startAt=**20**&maxResults=100&expand=log Desired Output string myurl.com/search?project=ABC&startAt=**120**&maxResults=100&expand=log Actual Output string myurl.com/search?project=ABC&startAt=**$cntr**&maxResults=100&expand=log Code: $str='myurl.com/search?project=ABC&startAt=20&maxResults=100&expand=log' $cntr=120 $str = $str -replace '^(.+&startAt=)(\d+)(&.+)$', '$1$cntr$3' $str
You need to Use double quotes to be able to use string interpolation Use the unambiguous backreference syntax, ${n}, where n is the group ID. In this case, you can use PS C:\Users\admin> $str='myurl.com/search?project=ABC&startAt=20&maxResults=100&expand=log' PS C:\Users\admin> $cntr=120 PS C:\Users\admin> $str = $str -replace '^(.+&startAt=)(\d+)(&.+)$', "`${1}$cntr`$3" PS C:\Users\admin> $str myurl.com/search?project=ABC&startAt=120&maxResults=100&expand=log See the .NET regex "Substituting a Numbered Group" documentation: All digits that follow $ are interpreted as belonging to the number group. If this is not your intent, you can substitute a named group instead. For example, you can use the replacement string ${1}1 instead of $11 to define the replacement string as the value of the first captured group along with the number "1".
A couple things here: If you just add the "12" you end up with $112$3 which isn't what you want. What I did was appended a slash in front and then removed it on the backend, so the replace becomes $1\12$3. $str='myurl.com/search?project=ABC&startAt=20&maxResults=100&expand=log' $cntr=12 $str = ($str -replace '^(.+&startAt=)(\d+)(&.+)$', ('$1\' + $cntr.ToString() +'$3')).Replace("\", "") $str Looking to see if there's another way to add the literal "12" in the replace section with the extra character, but this does work. Here's another way to do it where you have a literal string between the $1 and $3 and then replace that at the end. $str='myurl.com/search?project=ABC&startAt=20&maxResults=100&expand=log' $cntr=12 $str = ($str -replace '^(.+&startAt=)(\d+)(&.+)$', ('$1REPLACECOUNTER$3')).Replace("REPLACECOUNTER", "$cntr") $str
extract string between two dots
I have a string of the following format: word1.word2.word3 What are the ways to extract word2 from that string in perl? I tried the following expression but it assigns 1 to sub: #perleval $vars{sub} = $vars{string} =~ /.(.*)./; 0# EDIT: I have tried several suggestions, but still get the value of 1. I suspect that the entire expression above has a problem in addition to parsing. However, when I do simple assignment, I get the correct result: #perleval $vars{sub} = $vars{string} ; 0# assigns word1.word2.word3 to variable sub
. has a special meaning in regular expressions, so it needs to be escaped. .* could match more than intended. [^.]* is safer. The match operator (//) simply returns true/false in scalar context. You can use any of the following: $vars{sub} = $vars{string} =~ /\.([^.]*)\./ ? $1 : undef; $vars{sub} = ( $vars{string} =~ /\.([^.]*)\./ )[0]; ( $vars{sub} ) = $vars{string} =~ /\.([^.]*)\./; The first one allows you to provide a default if there's no match.
Try: /\.([^\.]+)\./ . has a special meaning and would need to be escaped. Then you would want to capture the values between the dots, so use a negative character class like ([^\.]+) meaning at least one non-dot. if you use (.*) you will get: word1.stuff1.stuff2.stuff3.word2 to result in: stuff1.stuff2.stuff3 But maybe you want that? Here is my little example, I do find the perl one liners a little harder to read at times so I break it out: use strict; use warnings; if ("stuff1.stuff2.stuff3" =~ m/\.([^.]+)\./) { my $value = $1; print $value; } else { print "no match"; } result stuff2
. has a special meaning: any character (see the expression between your parentheses) Therefore you have to escape it (\.) if you search a literal dot: /\.(.*)\./
You've got to make sure you're asking for a list when you do the search. my $x= $string =~ /look for (pattern)/ ; sets $x to 1 my ($x)= $string =~ /look for (pattern)/ ; sets $x to pattern.
Perl how do you assign a varanble to a regex match result
How do you create a $scalar from the result of a regex match? Is there any way that once the script has matched the regex that it can be assigned to a variable so it can be used later on, outside of the block. IE. If $regex_result = blah blah then do something. I understand that I should make the regex as non-greedy as possible. #!/usr/bin/perl use strict; use warnings; # use diagnostics; use Win32::OLE; use Win32::OLE::Const 'Microsoft Outlook'; my #Qmail; my $regex = "^\\s\*owner \#"; my $sentence = $regex =~ "/^\\s\*owner \#/"; my $outlook = Win32::OLE->new('Outlook.Application') or warn "Failed Opening Outlook."; my $namespace = $outlook->GetNamespace("MAPI"); my $folder = $namespace->Folders("test")->Folders("Inbox"); my $items = $folder->Items; foreach my $msg ( $items->in ) { if ( $msg->{Subject} =~ m/^(.*test alert) / ) { my $name = $1; print " processing Email for $name \n"; push #Qmail, $msg->{Body}; } } for(#Qmail) { next unless /$regex|^\s*description/i; print; # prints what i want ie lines that start with owner and description } print $sentence; # prints ^\\s\*offense \ # not lines that start with owner.
One way is to verify a match occurred. use strict; use warnings; my $str = "hello what world"; my $match = 'no match found'; my $what = 'no what found'; if ( $str =~ /hello (what) world/ ) { $match = $&; $what = $1; } print '$match = ', $match, "\n"; print '$what = ', $what, "\n";
Use Below Perl variables to meet your requirements - $` = The string preceding whatever was matched by the last pattern match, not counting patterns matched in nested blocks that have been exited already. $& = Contains the string matched by the last pattern match $' = The string following whatever was matched by the last pattern match, not counting patterns matched in nested blockes that have been exited already. For example: $_ = 'abcdefghi'; /def/; print "$`:$&:$'\n"; # prints abc:def:ghi
The match of a regex is stored in special variables (as well as some more readable variables if you specify the regex to do so and use the /p flag). For the whole last match you're looking at the $MATCH (or $& for short) variable. This is covered in the manual page perlvar. So say you wanted to store your last for loop's matches in an array called #matches, you could write the loop (and for some reason I think you meant it to be a foreach loop) as: my #matches = (); foreach (#Qmail) { next unless /$regex|^\s*description/i; push #matches_in_qmail $MATCH print; } I think you have a problem in your code. I'm not sure of the original intention but looking at these lines: my $regex = "^\\s\*owner \#"; my $sentence = $regex =~ "/^\s*owner #/"; I'll step through that as: Assign $regexto the string ^\s*owner #. Assign $sentence to value of running a match within $regex with the regular expression /^s*owner $/ (which won't match, if it did $sentence will be 1 but since it didn't it's false). I think. I'm actually not exactly certain what that line will do or was meant to do.
I'm not quite sure what part of the match you want: the captures, or something else. I've written Regexp::Result which you can use to grab all the captures etc. on a successful match, and Regexp::Flow to grab multiple results (including success statuses). If you just want numbered captures, you can also use Data::Munge
You can do the following: my $str ="hello world"; my ($hello, $world) = $str =~ /(hello)|(what)/; say "[$_]" for($hello,$world); As you see $hello contains "hello".
If you have older perl on your system like me, perl 5.18 or earlier, and you use $ $& $' like codequestor's answer above, it will slow down your program. Instead, you can use your regex pattern with the modifier /p, and then check these 3 variables: ${^PREMATCH}, ${^MATCH}, and ${^POSTMATCH} for your matching results.
How can I extract a substring up to the first digit?
How can I find the first substring until I find the first digit? Example: my $string = 'AAAA_BBBB_12_13_14' ; Result expected: 'AAAA_BBBB_'
Judging from the tags you want to use a regular expression. So let's build this up. We want to match from the beginning of the string so we anchor with a ^ metacharacter at the beginning We want to match anything but digits so we look at the character classes and find out this is \D We want 1 or more of these so we use the + quantifier which means 1 or more of the previous part of the pattern. This gives us the following regular expression: ^\D+ Which we can use in code like so: my $string = 'AAAA_BBBB_12_13_14'; $string =~ /^\D+/; my $result = $&;
Most people got half of the answer right, but they missed several key points. You can only trust the match variables after a successful match. Don't use them unless you know you had a successful match. The $&, $``, and$'` have well known performance penalties across all regexes in your program. You need to anchor the match to the beginning of the string. Since Perl now has user-settable default match flags, you want to stay away from the ^ beginning of line anchor. The \A beginning of string anchor won't change what it does even with default flags. This would work: my $substring = $string =~ m/\A(\D+)/ ? $1 : undef; If you really wanted to use something like $&, use Perl 5.10's per-match version instead. The /p switch provides non-global-perfomance-sucking versions: my $substring = $string =~ m/\A\D+/p ? ${^MATCH} : undef; If you're worried about what might be in \D, you can specify the character class yourself instead of using the shortcut: my $substring = $string =~ m/\A[^0-9]+/p ? ${^MATCH} : undef; I don't particularly like the conditional operator here, so I would probably use the match in list context: my( $substring ) = $string =~ m/\A([^0-9]+)/; If there must be a number in the string (so, you don't match an entire string that has no digits, you can throw in a lookahead, which won't be part of the capture: my( $substring ) = $string =~ m/\A([^0-9]+)(?=[0-9])/;
$str =~ /(\d)/; print $`; This code print string, which stand before matching
perl -le '$string=q(AAAA_BBBB_12_13_14);$string=~m{(\D+)} and print $1' AAAA_BBBB_
How do I use Perl to intersperse characters between consecutive matches with a regex substitution?
The following lines of comma-separated values contains several consecutive empty fields: $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n" I want to replace these empty fields with 'N/A' values, which is why I decided to do it via a regex substitution. I tried this first of all: $rawdata =~ s/,([,\n])/,N\/A/g; # RELABEL UNAVAILABLE DATA AS 'N/A' which returned 2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,,N/A,\n Not what I wanted. The problem occurs when more than two consecutive commas occur. The regex gobbles up two commas at a time, so it starts at the third comma rather than the second when it rescans the string. I thought this could be something to do with lookahead vs. lookback assertions, so I tried the following regex out: $rawdata =~ s/(?<=,)([,\n])|,([,\n])$/,N\/A$1/g; # RELABEL UNAVAILABLE DATA AS 'N/A' which resulted in: 2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,N/A,Clear\n 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,,N/A,,N/A\n That didn't work either. It just shifted the comma-pairings by one. I know that washing this string through the same regex twice will do it, but that seems crude. Surely, there must be a way to get a single regex substitution to do the job. Any suggestions? The final string should look like this: 2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,N/A,Clear\n 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,N/A,,N/A,N/A,N/A,N/A,N/A\n
EDIT: Note that you could open a filehandle to the data string and let readline deal with line endings: #!/usr/bin/perl use strict; use warnings; use autodie; my $str = <<EO_DATA; 2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,, EO_DATA open my $str_h, '<', \$str; while(my $row = <$str_h>) { chomp $row; print join(',', map { length $_ ? $_ : 'N/A'} split /,/, $row, -1 ), "\n"; } Output: E:\Home> t.pl 2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A You can also use: pos $str -= 1 while $str =~ s{,(,|\n)}{,N/A$1}g; Explanation: When s/// finds a ,, and replaces it with ,N/A, it has already moved to the character after the last comma. So, it will miss some consecutive commas if you only use $str =~ s{,(,|\n)}{,N/A$1}g; Therefore, I used a loop to move pos $str back by a character after each successful substitution. Now, as #ysth shows: $str =~ s!,(?=[,\n])!,N/A!g; would make the while unnecessary.
I couldn't quite make out what you were trying to do in your lookbehind example, but I suspect you are suffering from a precedence error there, and that everything after the lookbehind should be enclosed in a (?: ... ) so the | doesn't avoid doing the lookbehind. Starting from scratch, what you are trying to do sounds pretty simple: place N/A after a comma if it is followed by another comma or a newline: s!,(?=[,\n])!,N/A!g; Example: my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"; use Data::Dumper; $Data::Dumper::Useqq = $Data::Dumper::Terse = 1; print Dumper($rawData); $rawData =~ s!,(?=[,\n])!,N/A!g; print Dumper($rawData); Output: "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n" "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,N/A,Clear\n2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,N/A,N/A,N/A,N/A\n"
You could search for (?<=,)(?=,|$) and replace that with N/A. This regex matches the (empty) space between two commas or between a comma and end of line.
The quick and dirty hack version: my $rawData = "2008-02-06,8:00 AM,14.0,6.0,59,1027,-9999.0,West,6.9,-,N/A,,Clear 2008-02-06,9:00 AM,16,6,40,1028,12,WNW,10.4,,,,\n"; while ($rawData =~ s/,,/,N\/A,/g) {}; print $rawData; Not the fastest code, but the shortest. It should loop through at max twice.
Not a regex, but not too complicated either: $string = join ",", map{$_ eq "" ? "N/A" : $_} split (/,/, $string,-1); The ,-1 is needed at the end to force split to include any empty fields at the end of the string.