【问题标题】:Splitting a string while keeping the delimiters except escaped ones (regex)拆分字符串,同时保留除转义的分隔符(正则表达式)
【发布时间】:2013-07-06 22:06:41
【问题描述】:

如果我有一个由字符分隔的字符串,让我们这样说:

a-b-c

我想保留分隔符,我可以使用后视和前瞻来保留分隔符本身,例如:

string.split("((?<=-)|(?=-))");

导致

  • a
  • -
  • b
  • -
  • c

现在,如果其中一个分隔符被转义,像这样:

a-b\-c

我想纪念逃跑,我想出使用这样的正则表达式:

((?<=-(?!(?<=\\-))) | (?=-(?!(?<=\\-))))  

ergo

string.split("((?<=-(?!(?<=\\\\-)))|(?=-(?!(?<=\\\\-))))"):

现在,这有效并导致:

  • a
  • -
  • b\-c

(我稍后会用string.replace("\\", ""); 删除反斜杠,我还没有找到将其包含在正则表达式中的方法)

我的问题是理解问题。
按照我的理解,正则表达式将是

split ((if '-' is before (unless ('\-' is before))) or (if '-' is after (unless ('\-' is before))))

为什么最后一部分不应该是“除非\ 在之前”?如果 '-' 在之后,这意味着我们在 '\' 和 '-' 之间,所以只有 \ 应该在之前,而不是 \\-,但是如果我更改正则表达式以反映它,它就不起作用这个:

((?<=-(?!(?<=\\-))) | (?=-(?!(?<=\\))))  

结果:a-b\-c

这是什么原因?我的推理错误在哪里?

【问题讨论】:

  • 你知道,也有负面的后视

标签: java regex regex-negation lookahead lookbehind


【解决方案1】:

虽然这并不能真正回答问题,但它解释了环视的工作原理。

Lookarounds 是锚点:它们不消耗文本,而是在输入文本中找到一个位置。您的正则表达式可以用更简单的方式编写:

(?<=-)(?<!\\-)|(?=-)(?<!\\)

这里有所有四种环视:正面和负面的lookbehind,正面和负面的lookahead。

完整的正则表达式如下:

(?<=-)            # Find a position where what precedes is a dash
(?<!\\-)          # Find a position where what precedes is not \-
|                 # Or
(?=-)             # Find a position where what follows is a dash
(?<!\\)           # Find a position where what precedes is not a \

请注意术语“位置”。请注意,锚点根本不会在文本中前进。

现在,如果我们尝试将该正则表达式与 a-b\-c 匹配:

# Step 1
# Input:    | a-b\-c|
# Position: |^      |
# Regex:    | (?<=-)(?<!\\-)|(?=-)(?<!\\)|
# Position: |^                           |
# No match, try other alternative
# Input:    | a-b\-c|
# Position: |^      |
# Regex:    |(?<=-)(?<!\\-)| (?=-)(?<!\\)|
# Position: |               ^            |
# No match, regex fails
# Advance one position in the input text and try again

# Step 2
# Input:    |a -b\-c|
# Position: | ^     |
# Regex:    | (?<=-)(?<!\\-)|(?=-)(?<!\\)|
# Position: |^                           |
# No match, try other alternative
# Input:    |a -b\-c|
# Position: | ^     |
# Regex:    |(?<=-)(?<!\\-)| (?=-)(?<!\\)|
# Position: |               ^            |
# Match: a "-" follows
# Input:    |a -b\-c|
# Position: | ^     |
# Regex:    |(?<=-)(?<!\\-)|(?=-) (?<!\\)|
# Position: |                    ^       |
# Match: what precedes is not a \
# Input:    |a -b\-c|
# Position: | ^     |
# Regex:    |(?<=-)(?<!\\-)|(?=-)(?<!\\) |
# Position: |                           ^|
# Regex is satisfied

这是一个不使用拆分且不使用环视的替代方案:

[a-z]+(\\-[a-z]+)*|-

您可以在Pattern 中使用此正则表达式并使用Matcher

public static void main(final String... args)
{
    final Pattern pattern
        = Pattern.compile("[a-z]+(\\\\-[a-z]+)*|-");

    final Matcher m = pattern.matcher("a-b\\-c");
    while (m.find())
        System.out.println(m.group());
}

【讨论】:

  • 您对正则表达式的简化是我最喜欢的一种,因为它最容易理解。感谢您的长篇解释,非常感谢。我会把你的答案标记为正确的,但 Pshemo 确实直接回答了这个问题,所以公平地说,它属于他。希望你不要介意。
  • 我不介意 ;) 最重要的是它现在对你有用 ;)
【解决方案2】:

为什么最后一部分不应该是“除非 \ 在之前”?

(?=-(?!(?<=\\-)))) 
    ^here

光标在- 之后,所以"unless \ is before" 总是错误的,因为我们在当前位置之前总是有-


也许更简单的正则表达式会是

(?&lt;=(?&lt;!\\\\)-)|(?=(?&lt;!\\\\)-)

  • (?&lt;=(?&lt;!\\\\)-) 将检查我们是否在 - 之后,之前没有 \
  • (?=(?&lt;!\\\\)-)将检查我们是否在-之前没有\

【讨论】:

  • 啊,我不知道 ?= 移动光标,我对正则表达式还是很陌生。此外,您更简单的示例看起来更好,谢谢!
  • @pemko BTW 所有look-around 机制都是零宽度的。这意味着他们可以在检查条件时根据需要移动光标,但在他们退出后,光标将重置到开始环视之前的位置。
猜你喜欢
  • 1970-01-01
  • 2014-11-22
  • 2011-02-24
  • 1970-01-01
  • 2012-08-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多