Regex: don't match string ending with newline

2019-02-27 12:05发布

I can't figure out how to match a string but not if it has a trailing newline character (\n), which seems automatically stripped:

import re

print(re.match(r'^foobar$', 'foobar'))
# <_sre.SRE_Match object; span=(0, 6), match='foobar'>

print(re.match(r'^foobar$', 'foobar\n'))
# <_sre.SRE_Match object; span=(0, 6), match='foobar'>

print(re.match(r'^foobar$', 'foobar\n\n'))
# None

For me, the second case should also return None.
When we set the end of a pattern with $, like ^foobar$, it should only match a string like foobar, not foobar\n.

What am I missing?

3条回答
Juvenile、少年°
2楼-- · 2019-02-27 12:17

The documentation says this about the $ character:

Matches the end of the string or just before the newline at the end of the string, and in MULTILINE mode also matches before a newline.

So, without the MULTILINE option, it matches exactly the first two strings you tried: 'foobar' and 'foobar\n', but not 'foobar\n\n', because that is not a newline at the end of the string.

On the other hand, if you choose MULTILINE option, it will match the end of any line:

>>> re.match(r'^foobar$', 'foobar\n\n', re.MULTILINE)
<_sre.SRE_Match object; span=(0, 6), match='foobar'>

Of course, this will also match in the following case, which may or may not be what you want:

>>> re.match(r'^foobar$', 'foobar\nanother line\n', re.MULTILINE)
<_sre.SRE_Match object; span=(0, 6), match='foobar'>

In order to NOT match the ending newline, use the negative lookahead as DeepSpace wrote.

查看更多
Viruses.
3楼-- · 2019-02-27 12:31

You more likely don't need $ but rather \Z:

>>> print(re.match(r'^foobar\Z', 'foobar\n'))
None
  • \Z matches only at the end of the string.
查看更多
何必那么认真
4楼-- · 2019-02-27 12:37

This is the defined behavior of $, as can be read in the docs that @zvone linked to or even on https://regex101.com:

$ asserts position at the end of the string, or before the line terminator right at the end of the string (if any)

You can use an explicit negative lookahead to counter this behavior:

import re

print(re.match(r'^foobar(?!\n)$', 'foobar'))
# <_sre.SRE_Match object; span=(0, 6), match='foobar'>

print(re.match(r'^foobar(?!\n)$', 'foobar\n'))
# None

print(re.match(r'^foobar(?!\n)$', 'foobar\n\n'))
# None
查看更多
登录 后发表回答