tags:

views:

97

answers:

2

I'm trying to match a "#" followed by letters if and only if it's preceded by newline, whitespace or is the first character in a string. The first two I've done, but I'm having a hard time matching if it's the first character in a string. I'm trying to find a use for '\A', but it doesn't work to just add it to the class containing newline and whitespace. What have I missed?

The regular expression I've come up with so far is:

from re import findall, escape
from string import punctuation, whitespace

NEWLINE = """\r\n?|\n"""
INVALID_TAG_CHARACTERS = escape(punctuation.replace('-', '').replace('_', '') + whitespace)
VALID_TAGS = r'[\s%s]+#[^%s]+' % (NEWLINE, INVALID_TAG_CHARACTERS)
tags = findall(VALID_TAGS, text)
+1  A: 

Turn on the multi-line flag, so ^ matches the position after a newline, then just use:

re.compile(r"(?m)^\s*#") # includes the flag for multi-line

Or

re.compile(r"(?m)^\s*#.*$")

to get the full line (with dot matching newline mode disabled).

For the "first character in string", that depends on what a string is defined as - you may need to use a full parser for this, rather than a single regex.

Peter Boughton
ah missed the "first character in string bit"... have edited answer to include that, but not sure if it's very useful. :S
Peter Boughton
`^` It doesn't match a newline, it matches the imaginary gap *between* a newline and the next character (in multiline mode, that is). You probably knew that already, but I couldn't let that statement go unchallenged.
Alan Moore
Good point, it is an important distinction that `^` and `$` match the positions, not consuming actual characters.
Peter Boughton
Can't seem to make either of these lines work. :-/But thanks for introducing me to MULTILINE. :-)
MdaG
+2  A: 

I think this is what you're looking for:

result = re.findall("(?:^|\s)(#[a-zA-Z]+)", text, re.MULTILINE)

The (?:^|\s) is a set of non-grouping parentheses (we don't want this part in our results). With the multiline flag, it will match the beginning of the string, or a preceding newline or whitespace. The next group is your 'tag,' I believe. If it's other than letters following the #, you'll have to fiddle with that second group.

FellowMD
This worked like a charm, thank you! :-)I had to change it to "(?:^|\s)(#[^%s]+) % INVALID_TAG_CHARACTERS" though.
MdaG
@MdaG, you may want to use `re.escape(INVALID_TAG_CHARACTERS)` to ensure you don't accidentally inject regex special-characters into your pattern.
Alec Thomas
Isn't the escape in the original post enough?i.e.) INVALID_TAG_CHARACTERS = escape(punctuation.replace('-', '').replace('_', '') + whitespace)
MdaG