Simple regex - finding words including numbers but only on occasion - regex

I'm really bad at regex, I have:
/(#[A-Za-z-]+)/
which finds words after the # symbol in a textbox, however I need it to ignore email addresses, like:
foo#things.com
however it finds #things
I also need it to include numbers, like:
#He2foo
however it only finds the #He part.
Help is appreciated, and if you feel like explaining regex in simple terms, that'd be great :D

/(?:^|(?<=\s))#([A-Za-z0-9]+)(?=[.?]?\s)/
#This (matched) regex ignores#this but matches on #separate tokens as well as tokens at the end of a sentence like #this. or #this? (without picking the . or the ?) And yes email#addresses.com are ignored too.
The regex while matching on # also lets you quickly access what's after it (like userid in #userid) by picking up the regex group(1). Check PHP documentation on how to work with regex groups.

You can just add 0-9 to your regex, like so:
/(#[A-Za-z0-9-]+)/
Don't think any more explanation is needed since you've been able to come this far by yourself. 0-9 is just like a-z (though numeric ofcourse).
In order to ignore emailaddresses you will need to provide more specific requirements. You could try preceding # with (^| ) which basically states that your value MUST be preceeded by either the start of the string (so nothing really, though at the start) or a space.
Extending this you can also use ($| ) on the end to require the value to be followed by the end of the string or a space (which means there's no period allowed, which is requirement for a valid emailaddress).
Update
$subject = "#a #b a#b a# #b";
preg_match_all("/(^| )#[A-Za-z0-9-]+/", $subject, $matches);
print_r($matches[0]);

Related

How do I match a colon with no values after it?

I'm new to the website and to Regular Expression as well.
So I want to bookmark a list of Emails that have no value after the colons ":" as highlighted in the picture below.
Here is an example:
abcdef#gmail.com:123456
abcdEF452#gmail.com:test123##NEW
abcdef#gmail.com:
abcdef#gmail.com:
I only want to bookmark the last two ones so it would be like this:
abcdef#gmail.com:
abcdef#gmail.com:
The following regex will match the "pre-colon" pattern if and only if it is followed by nothing but whitespace until the end of the line:
\w+#\w+\.\w+:\s*$
View on regex101
Note that matching email addresses with 100% correctness is more complicated than this, but this will likely do for your use case.
If you only want to find strings that end with a colon, then all you need is :$.
I find this request a bit odd, perhaps if you could elaborate a bit more on your use case I may be able to provide a better approach or solution.
Now, I think that this expression should work the way you expect:
[\w\.]+#[a-z0-9][a-z0-9-]*[a-z0-9]?
Add the colon sign at the end if you need to match for the colon sign as well.
I noticed that the other proposed expressions don't account for email addresses with a dot in the username part or with dashed in the domain part. You may use a combination of all the solutions if you are more familiar with RegEx. I highly recommend you test the expression before moving it to production, you can do further tests easily on this page https://regexr.com/.
[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?
Will be a more adequate RegEx since the Internet Engineering Task Force established limits on how an email address can be formatted and this accounts for those additional characters. More details on this page https://www.mailboxvalidator.com/resources/articles/acceptable-email-address-syntax-rfc/.
As a friendly reminder, Stack Overflow can be best used when you have already invested some effort in fixing some problem, rather than having a community member provide you with a straight answer. This and other suggestions are listed on this other page https://stackoverflow.com/tour.
Try this:
[a-zA-Z]+#[a-zA-Z]+: # Only a-zA-Z, numbers are not accepted
Note: the last character is a space " "
[\w+]+#[\w+]+: # \w+ = Matches one or more [A-Za-z0-9_]
Without a space it will matches only these with no character after the colon.
[\w+]+#[\w+]+.*:$ # Matches only when there is also .XXX. For example: .com or .de
Given this:
abcdEF452#gmail.com:test123##NEW
There are three parts to this:
Before the #.
Between the # and the :
After the :
If we assume (1) has to be there and not empty.
If we assume (2) has to be there and not empty.
If we assume (3) the ':' is required by the trailing part can be empty.
I don't want to make assumptions about other requirements.
Then I would use:
[^#]+#[^:]+:.*$
Meaning:
[^#] => Anything apart from the '#' character.
[^#]+ => The above 1 or more times.
[^#]+# => The above followed by '#' character.
[^:] => Anything apart from the ':' character.
[^:]+ => The above 1 or more times.
[^:]+: => The above followed by ':' character.
.* => Any character 0 or more times.
$ end of line.
So if we want to mkae sure we only find things that don't have anything after the ':' we can simplify a bit.
[^#]+#[^:]+:$
Make sure we have the '#' and ':' parts and they are none empty. But the colon is followed by the end of line.
If you don't care about part (1) or (2) we can simplify even more.
[^:]+:$
Line must contain a : don't care what is in front as long as there is a least one character before the ':' and zero after.
Final simplification.
:$
If you don't care about anything except that the colon is not followed by anything.

Regex to match(extract) string between dot(.)

I want to select some string combination (with dots(.)) from a very long string (sql). The full string could be a single line or multiple line with new line separator, and this combination could be in start (at first line) or a next line (new line) or at both place.
I need help in writing a regex for it.
Examples:
String s = I am testing something like test.test.test in sentence.
Expected output: test.test.test
Example2 (real usecase):
UPDATE test.table
SET access = 01
WHERE access IN (
SELECT name FROM project.dataset.tablename WHERE name = 'test' GROUP BY 1 )
Expected output: test.table and project.dataset.tablename
, can I also add some prefix or suffix words or space which should be present where ever this logic gets checked. In above case if its update regex should pick test.table, but if the statement is like select test.table regex should not pick it up this combinations and same applies for suffix.
Example3: This is to illustrate the above theory.
INS INTO test.table
SEL 'abcscsc', wu_id.Item_Nbr ,1
FROM test.table as_t
WHERE as_t.old <> 0 AND as_t.date = 11
AND (as_t.numb IN ('11') )
Expected Output: test.table, test.table (Key words are INTO and FROM)
Things Not Needed in selection:as_t.numb, as_t.old, as_t.date
If I get the regex I can use in program to extract this word.
Note: Before and after string words to the combination could be anything like update, select { or(, so we have to find the occurrence of words which are joined together with .(dot) and all the number of such occurrence.
I tried something like this:
(?<=.)(.?)(?=.)(.?) -: This only selected the word between two .dot and not all.
.(?<=.)(.?)(?=.)(.?). - This everything before and after.
To solve your initial problem, we can just use some negation. Here's the pattern I came up with:
[^\s]+\.[^\s]+
[^ ... ] Means to make a character class including everything except for what's between the brackets. In this case, I put \s in there, which matches any whitespace. So [^\s] matches anything that isn't whitespace.
+ Is a quantifier. It means to find as many of the preceding construct as you can without breaking the match. This would happily match everything that's not whitespace, but I follow it with a \., which matches a literal .. The \ is necessary because . means to match any character in regex, so we need to escape it so it only has its literal meaning. This means there has to be a . in this group of non-whitespace characters.
I end the pattern with another [^\s]+, which matches everything after the . until the next whitespace.
Now, to solve your secondary problem, you want to make this match only work if it is preceded by a given keyword. Luckily, regex has a construct almost specifically for this case. It's called a lookbehind. The syntax is (?<= ... ) where the ... is the pattern you want to look for. Using your example, this will only match after the keywords INTO and FROM:
(?<=(?:INTO|FROM)\s)[^\s]+\.[^\s]+
Here (?:INTO|FROM) means to match either the text INTO or the text FROM. I then specify that it should be followed by a whitespace character with \s. One possible problem here is that it will only match if the keywords are written in all upper case. You can change this behavior by specifying the case insensitive flag i to your regex parser. If your regex parser doesn't have a way to specify flags, you can usually still specify it inline by putting (?i) in front of the pattern, like so:
(?i)(?<=(?:INTO|FROM)\s)[^\s]+\.[^\s]+
If you are new to regex, I highly recommend using the www.regex101.com website to generate regex and learn how it works. Don't forget to check out the code generator part for getting the regex code based on the programming language you are using, that's a cool feature.
For your question, you need a regex that understands any word character \w that matches between 0 and unlimited times, followed by a dot, followed by another series of word character that repeats between 0 and unlimited times.
So here is my solution to your question:
Your regex in JavaScript:
const regex = /([\w][.][\w])+/gm;
in Java:
final String regex = "([\w][.][\w])+";
in Python:
regex = r"([\w][.][\w])+"
in PHP:
$re = '/([\w][.][\w])+/m';
Note that: this solution is written for your use case (to be used for SQL strings), because now if you have something like '.word' or 'word..word', it will still catch it which I assume you don't have a string like that.
See this screenshot for more details

Regex for matching last two parts of a URL

I am trying to figure out the best regex to simply match only the last two strings in a url.
For instance with www.stackoverflow.com I just want to match stackoverflow.com
The issue i have is some strings can have a large number of periods for instance
a-abcnewsplus.i-a277eea3.rtmp.atlas.cdn.yimg.com
should also return only yimg.com
The set of URLS I am working with does not have any of the path information so one can assume the last part of the string is always .org or .com or something of that nature.
What regular expresion will return stackoverflow.com when run against www.stackoverflow.com and will return yimg.com when run against a-abcnewsplus.i-a277eea3.rtmp.atlas.cdn.yimg.com
under the condtions above?
You don't have to use regex, instead you can use a simple explode function.
So you're looking to split your URL at the periods, so something like
$url = "a-abcnewsplus.i-a277eea3.rtmp.atlas.cdn.yimg.com";
$url_split = explode(".",$url);
And then you need to get the last two elements, so you can echo them out from the array created.
//this will return the second to last element, yimg
echo $url_split[count($url_split)-2];
//this will echo the period
echo ".";
//this will return the last element, com
echo $url_split[count($url_split)-1];
So in the end you'll get yimg.com as the final output.
Hope this helps.
I don't know what did you try so far, but I can offer the following solution:
/.*?([\w]+\.[\w]+)$/
There are a couple of tricks here:
Use $ to match till the end of the string. This way you'll be sure your regex engine won't catch the match from the very beginning.
Use grouping inside (...). In fact it means the following: match word that contains at least one letter then there should be a dot (backslashed because dot has a special meaning in regex and we want it 'as is' and then again series of letters with at least one of letters).
Use reluctant search in the beginning of the pattern, because otherwise it will match everything in a greedy manner, for example, if your text is :
abc.def.gh
the greedy match will give f.gh in your group, and its not what you want.
I assumed that you can have only letters in your host (\w matches the word, maybe in your example you will need something more complicated).
I post here a working groovy example, you didn't specify the language you use but the engine should be similar.
def s = "abc.def.gh"
def m = s =~/.*?([\w]+\.[\w]+)$/
println m[0][1] // outputs the first (and the only you have) group in groovy
Hope this helps
if you needed a solution in a Perl Regular Expression compatible way that will work in a number of languages, you can use something like that - the example is in PHP
$url = "a-abcnewsplus.i-a277eea3.rtmp.atlas.cdn.yimg.com";
preg_match('|[a-zA-Z-0-9]+\.[a-zA-Z]{2,3}$|', $url, $m);
print($m[0]);
This regex guarantees you to fetch the last part of the url + domain name. For example, with a-abcnewsplus.i-a277eea3.rtmp.atlas.cdn.yimg.com this produces
yimg.com
as an output, and with www.stackoverflow.com (with or without preceding triple w) it gives you
stackoverflow.com
as a result
A shorter version
/(\.[^\.]+){2}$/

How to ignore whitespace in a regular expression subject string?

Is there a simple way to ignore the white space in a target string when searching for matches using a regular expression pattern? For example, if my search is for "cats", I would want "c ats" or "ca ts" to match. I can't strip out the whitespace beforehand because I need to find the begin and end index of the match (including any whitespace) in order to highlight that match and any whitespace needs to be there for formatting purposes.
You can stick optional whitespace characters \s* in between every other character in your regex. Although granted, it will get a bit lengthy.
/cats/ -> /c\s*a\s*t\s*s/
While the accepted answer is technically correct, a more practical approach, if possible, is to just strip whitespace out of both the regular expression and the search string.
If you want to search for "my cats", instead of:
myString.match(/m\s*y\s*c\s*a\*st\s*s\s*/g)
Just do:
myString.replace(/\s*/g,"").match(/mycats/g)
Warning: You can't automate this on the regular expression by just replacing all spaces with empty strings because they may occur in a negation or otherwise make your regular expression invalid.
Addressing Steven's comment to Sam Dufel's answer
Thanks, sounds like that's the way to go. But I just realized that I only want the optional whitespace characters if they follow a newline. So for example, "c\n ats" or "ca\n ts" should match. But wouldn't want "c ats" to match if there is no newline. Any ideas on how that might be done?
This should do the trick:
/c(?:\n\s*)?a(?:\n\s*)?t(?:\n\s*)?s/
See this page for all the different variations of 'cats' that this matches.
You can also solve this using conditionals, but they are not supported in the javascript flavor of regex.
You could put \s* inbetween every character in your search string so if you were looking for cat you would use c\s*a\s*t\s*s\s*s
It's long but you could build the string dynamically of course.
You can see it working here: http://www.rubular.com/r/zzWwvppSpE
If you only want to allow spaces, then
\bc *a *t *s\b
should do it. To also allow tabs, use
\bc[ \t]*a[ \t]*t[ \t]*s\b
Remove the \b anchors if you also want to find cats within words like bobcats or catsup.
This approach can be used to automate this
(the following exemplary solution is in python, although obviously it can be ported to any language):
you can strip the whitespace beforehand AND save the positions of non-whitespace characters so you can use them later to find out the matched string boundary positions in the original string like the following:
def regex_search_ignore_space(regex, string):
no_spaces = ''
char_positions = []
for pos, char in enumerate(string):
if re.match(r'\S', char): # upper \S matches non-whitespace chars
no_spaces += char
char_positions.append(pos)
match = re.search(regex, no_spaces)
if not match:
return match
# match.start() and match.end() are indices of start and end
# of the found string in the spaceless string
# (as we have searched in it).
start = char_positions[match.start()] # in the original string
end = char_positions[match.end()] # in the original string
matched_string = string[start:end] # see
# the match WITH spaces is returned.
return matched_string
with_spaces = 'a li on and a cat'
print(regex_search_ignore_space('lion', with_spaces))
# prints 'li on'
If you want to go further you can construct the match object and return it instead, so the use of this helper will be more handy.
And the performance of this function can of course also be optimized, this example is just to show the path to a solution.
The accepted answer will not work if and when you are passing a dynamic value (such as "current value" in an array loop) as the regex test value. You would not be able to input the optional white spaces without getting some really ugly regex.
Konrad Hoffner's solution is therefore better in such cases as it will strip both the regest and test string of whitespace. The test will be conducted as though both have no whitespace.

Regex to detect one of several strings

I've got a list of email addresses belonging to several domains. I'd like a regex that will match addresses belonging to three specific domains (for this example: foo, bar, & baz)
So these would match:
a#foo
a#bar
b#baz
This would not:
a#fnord
Ideally, these would not match either (though it's not critical for this particular problem):
a#foobar
b#foofoo
Abstracting the problem a bit: I want to match a string that contains at least one of a given list of substrings.
Use the pipe symbol to indicate "or":
/a#(foo|bar|baz)\b/
If you don't want the capture-group, use the non-capturing grouping symbol:
/a#(?:foo|bar|baz)\b/
(Of course I'm assuming "a" is OK for the front of the email address! You should replace that with a suitable regex.)
^(a|b)#(foo|bar|baz)$
if you have this strongly defined a list. The start and end character will only search for those three strings.
Use:
/#(foo|bar|baz)\.?$/i
Note the differences from other answers:
\.? - matching 0 or 1 dots, in case the domains in the e-mail address are "fully qualified"
$ - to indicate that the string must end with this sequence,
/i - to make the test case insensitive.
Note, this assumes that each e-mail address is on a line on its own.
If the string being matched could be anywhere in the string, then drop the $, and replace it with \s+ (which matches one or more white space characters)
should be more generic, the a shouldn't count, although the # should.
/#(foo|bar|baz)(?:\W|$)/
Here is a good reference on regex.
edit: change ending to allow end of pattern or word break. now assuming foo/bar/baz are full domain names.
If the previous (and logical) answers about '|' don't suit you, have a look at
http://metacpan.org/pod/Regex::PreSuf
module description : create regular expressions from word lists
Ok I know you asked for a regex answer.
But have you considered just splitting the string with the '#' char
taking the second array value (the domain)
and doing a simple match test
if (splitString[1] == "foo" && splitString[1] == "bar" && splitString[1] == "baz")
{
//Do Something!
}
Seems to me that RegEx is overkill. Of course my assumption is that your case is really as simple as you have listed.
You don't need a regex to find whether a string contains at least one of a given list of substrings. In Python:
def contain(string_, substrings):
return any(s in string_ for s in substrings)
The above is slow for a large string_ and many substrings. GNU fgrep can efficiently search for multiple patterns at the same time.
Using regex
import re
def contain(string_, substrings):
regex = '|'.join("(?:%s)" % re.escape(s) for s in substrings)
return re.search(regex, string_) is not None
Related
Multiple Skip Multiple Pattern Matching Algorithm (MSMPMA) [pdf]