Looking for a regular expression including alphanumeric + "&" and ";" - python

Here's the problem:
split=re.compile('\\W*')
This regular expression works fine when dealing with regular words, but there are occasions where I need the expression to include words like käyttäj&aml;auml;.
What should I add to the regex to include the & and ; characters?

I would treat the entities as a unit (since they also can contain numerical character codes), resulting in the following regular expression:
(\w|&(#(x[0-9a-fA-F]+|[0-9]+)|[a-z]+);)+
This matches
either a word character (including “_”), or
an HTML entity consisting of
the character “&”,
the character “#”,
the character “x” followed by at least one hexadecimal digit, or
at least one decimal digit, or
at least one letter (= named entity),
a semicolon
at least once.
/EDIT: Thanks to ΤΖΩΤΖΙΟΥ for pointing out an error.

You probably want to take the problem reverse, i.e. finding all the character without the spaces:
[^ \t\n]*
Or you want to add the extra characters:
[a-zA-Z0-9&;]*
In case you want to match HTML entities, you should try something like:
(\w+|&\w+;)*

you should make a character class that would include the extra characters. For example:
split=re.compile('[\w&;]+')
This should do the trick. For your information
\w (lower case 'w') matches word characters (alphanumeric)
\W (capital W) is a negated character class (meaning it matches any non-alphanumeric character)
* matches 0 or more times and + matches one or more times, so * will match anything (even if there are no characters there).

Looks like this RegEx did the trick:
split=re.compile('(\\\W+&\\\W+;)*')
Thanks for the suggestions. Most of them worked fine on Reggy, but I don't quite understand why they failed with re.compile.

Related

Regex Match on String (DOI)

Hi I'm struggling to understand why my Regex isn't working.
I have URL's that have DOI's on them like so:
https://link.springer.com/10.1007/s00737-021-01116-5
https://journals.sagepub.com/doi/pdf/10.1177/1078390319877228
https://journals.sagepub.com/doi/pdf/10.1177/1078390319877228
https://onlinelibrary.wiley.com/doi/10.1111/jocn.13435
https://journals.sagepub.com/doi/pdf/10.1177/1062860613484171
https://onlinelibrary.wiley.com/resolve/openurl?genre=article&title=Natural+Resources+Forum&issn=0165-0203&volume=26&date=2002&issue=1&spage=3
https://dx.doi.org/10.1108/14664100110397304?nols=y
https://onlinelibrary.wiley.com/doi/10.1111/jocn.15833
https://www.tandfonline.com/doi/pdf/10.1080/03768350802090592?needAccess=true
And I'm using for example this Regex, but it always returns empty?
print(re.findall(r'/^10.\d{4,9}/[-._;()/:A-Z0-9]+$/i', 'https://dx.doi.org/10.1108/02652320410549638?nols=y'))
Where have I gone wrong?
It looks like you come from another programming language that has the notion of regex literals that are delimited with forward slashes and have the modifiers following the closing slash (hence /i).
In Python there is no such thing, and these slashes and modifier(s) are taken as literal characters. For flags like i you can use the optional flags parameter of findall.
Secondly, ^ will match the start of the input string, but evidently the URLs you have as input do not start with 10, so that has to go. Instead you could require that the 10 must follow a word break... i.e. it should not be preceded by an alphanumerical character (or underscore).
Similarly, $ will match the end of the input string, but you have URLs that continue with URL parameters, like ?nols=y, so again the part you are interested in does not go on until the end of the input. So that has to go too.
The dot has a special meaning in regex, but you clearly intended to match a literal dot, so it should be escaped.
Finally, alphanumerical characters can be matched with \w, which also matches both lower case and capital Latin letters, so you can shorten the character class a bit and do without any flags such as i (re.I).
This leaves us with:
print(re.findall(r'\b10\.\d{4,9}/[-.;()/:\w]+',
'https://dx.doi.org/10.1108/02652320410549638?nols=y'))

Why does my Python regex not work as expected when including a forward slash?

I'm having a Python issue when I include a not / in my regex.
In the following example I only want to find a match if the string sitting in the first word boundary starts with a digit AND there isn't a / at any point afterwards.
Why does the following regex return 1ab as a group value? I was hoping it wouldn't find a match at all:
text = "1ab/"
regex = r"\b(\d[^/]*?)\b"
Whereas:
text = "1abc"
regex = r"\b(\d[^c]*?)\b"
does not return any match, which is the outcome I want for the / scenario.
Any help would be appreciated.
Thanks,
Roy
You can use a negative lookahead assertion:
r'\b(\d\w*?)\b(?!.*/)' (use flags=re.DOTALL with this or prepend (?s) to the regex)
(?!.*/) states that the rest of the input string does not contain a '/' character. If you don't want '/' to appear just as the next character, then use as the assertion (?!/).
You almost did it. Yet the slash is not alphanumerical and thus cannot be inside word . Therefore it makes no sense to match or prohibit it start and the end of the word. You have to place "not slash" sub-expression [^/] after the end of word. And add a star [^/]* (which matches the sequence of non-slash symbols) to address the case when slashes occurs toward the end of the string rather than immediately after the end of the first word.
Since you target the first word and absence of slash until the very end of string adding symbols of the start end might help. Especially, if you are use re.search. Resulting in
^[\W]*\b(\d\w*)\b[^/]*\Z
You can play with it using an online debugger such as https://regex101.com/r/uO27vU/2
to better understand the expression or tune it.
Above ^ is a start, \Z is the end of sting, \W is for "non-word" symbols, a \w is "word" symbol.
You can remove the first \b I kept it, as perhaps, it would easier for you to understand with it.
The second expression that you tried excludes words ending with c but first does not. ^c stands for any symbol but c and right after it you have \b which denotes the end of the word. Which reads please no "c"s at the end of the word.
Your first expression says pleas no slashes before the end of the word (sequence of alphanumeric) . Which is the case for you test.
Always use a debugger to get explanation of each symbol,test and
tune your expressions regex101.com/r/B6INGg/2
Note that the list of symbols in a word might be affected by flags. When the LOCALE and UNICODE flags are not specified, matches any alphanumeric character and the underscore; this is equivalent to the set [a-zA-Z0-9_].

Inverse regex match on group in Python

I see a lot of similarly worded questions, but I've had a strikingly difficult time coming up with the syntax for this.
Given a list of words, I want to print all the words that do not have special characters.
I have a regex which identifies words with special characters \w*[\u00C0-\u01DA']\w*. I've seen a lot of answers with fairly straightforward scenarios like a simple word. However, I haven't been able to find anything that negates a group - I've seen several different sets of syntax to include the negative lookahead ?!, but I haven't been able to come up with a syntax that works with it.
In my case given a string like: "should print nŌt thìs"
should print should and print but not the other two words. re.findall("(\w*[\u00C0-\u01DA']\w*)", paragraph.text) gives you the special characters - I just want to invert that.
For this particular case, you can simply specify the regular alphabet range in your search:
a = "should print nŌt thìs"
re.findall(r"(\b[A-Za-z]+\b)", a)
# ['should', 'print']
Of course you can add digits or anything else you want to match as well.
As for negative lookaheads, they use the syntax (?!...), with ? before !, and they must be in parentheses. To use one here, you can use:
r"\b(?!\w*[À-ǚ])\w*"
This:
Checks for a word boundary \b, like a space or the start of the input string.
Does the negative lookahead and stops the match if it finds any special character preceded by 0 or more word characters. You have to include the \w* because (?![À-ǚ]) would only check for the special character being the first letter in the word.
Finally, if it makes it past the lookahead, it matches any word characters.
Demo. Note in regex101.com you must specify Python flavor for \b to work properly with special characters.
There is a third option as well:
r"\b[^À-ǚ\s]*\b"
The middle part [^À-ǚ\s]* means match any character other than special characters or whitespace an unlimited number of times.
I know this is not a regex, but just a completely different idea you may not have had besides using regexes. I suppose it would be also much slower but I think it works:
>>> import unicodedata as ud
>>> [word for word in ['Cá', 'Lá', 'Aqui']\
if any(['WITH' in ud.name(letter) for letter in word])]
['Cá', 'Lá']
Or use ... 'WITH' not in to reverse.

Regex Pattern to Match Character Not Surrounded by Particular Characters

I'm trying to use a regular expression to match a certain character only when it isn't immediately adjacent to a certain character. (For an eBook in Calibre)
Specifically, I want to match all ” that aren't at the end of a sentence, which means they will be between regular characters, not an angle bracket or space. I thought ”[^<] would work, but that selects both the quotation mark and the next character, not just the character itself. I'm also not sure how to do an OR to check for a space. I'm assuming it would be something like ”[^<]|[^ ] but that's not right either.
Here's an example of what I would like to match:
Beside angle bracket:
<p class="calibre1">“I”m tired!”</p>
Beside space: <p class="calibre1">“I”m tired!” he said</p>
Only the quotation mark within I”m should be selected (and only the quotation mark itself)
I'm sorry if there's an obvious answer for this, but I've been reading over Python's regex documentation and I can't figure it out. :(
You can perhaps use a negative lookahead (?! ... ) like that:
”(?!<)
This will match ” unless it is followed by <.
To add the space...
”(?![< ])
That one will match ” unless followed by < or space.
Using a negative-lookahead:
regex = r '"(?!<|\s)'
| means "or"
\s means whitespace
You don't need to capture, since you know you're only matching a ".
Alternatively, you could use a character class instead of the or, ie: [<\s].

Can I mix character classes in Python RegEx?

Special sequences (character classes) in Python RegEx are escapes like \w or \d that matches a set of characters.
In my case, I need to be able to match all alpha-numerical characters except numbers.
That is, \w minus \d.
I need to use the special sequence \w because I'm dealing with non-ASCII characters and need to match symbols like "Æ" and "Ø".
One would think I could use this expression: [\w^\d] but it doesn't seem to match anything and I'm not sure why.
So in short, how can I mix (add/subtract) special sequences in Python Regular Expressions?
EDIT: I accidentally used [\W^\d] instead of [\w^\d]. The latter does indeed match something, including parentheses and commas which are not alpha-numerical characters as far as I'm concerned.
You can use r"[^\W\d]", ie. invert the union of non-alphanumerics and numbers.
You cannot subtract character classes, no.
Your best bet is to use the regex project, which offers additional functionality while remaining backwards compatible with the re module in in the standard library. It supports character classes based on Unicode properties:
\p{IsAlphabetic}
This will match any character that the Unicode specification states is an alphabetic character.
Even better, regex does support character class subtraction; it views such classes as sets and allows you to create a difference with the -- operator:
[\w--\d]
matches everything in \w except anything that also matches \d.
You can exclude classes using a negative lookahead assertion, such as r'(?!\d)[\w]' to match a word character, excluding digits. For example:
>>> re.search(r'(?!\d)[\w]', '12bac')
<_sre.SRE_Match object at 0xb7779218>
>>> _.group(0)
'b'
To exclude more than one group, you can use the usual [...] syntax in the lookahead assertion, for example r'(?![0-5])[\w]' would match any alphanumeric character except for digits 0-5.
As with [...], the above construct matches a single character. To match multiple characters, add a repetition operator:
>>> re.search(r'((?!\d)[\w])+', '12bac15')
<_sre.SRE_Match object at 0x7f44cd2588a0>
>>> _.group(0)
'bac'
I don't think you can directly combine (boolean and) character sets in a single regex, whether one is negated or not. Otherwise you could simply have combined [^\d] and \w.
Note: the ^ has to be at the start of the set, and applies to the whole set. From the docs: "If the first character of the set is '^', all the characters that are not in the set will be matched.".
Your set [\w^\d] tries to match an alpha-numerical character, followed by a caret, followed by a digit. I can imagine that doesn't match anything either.
I would do it in two steps, effectly combining the regular expressions. First match by non-digits (inner regex), then match by alpha-numerical characters:
re.search('\w+', re.search('([^\d]+)', s).group(0)).group(0)
or variations to this theme.
Note that would need to surround this with a try: except: block, as it will throw an AttributeError: 'NoneType' object has no attribute 'group' in case one of the two regexes fails. But you can, of course, split this single line up in a few more lines.

Categories