3

我们目前[^a-zA-Z0-9]在 Java 的replaceAll函数中使用从字符串中去除特殊字符。我们注意到,当连字符与数字混合时,我们需要允许它们。

匹配连字符的示例:

  • 1-2-3
  • -1-23-4562
  • --1---2--3---4-
  • --9--a--7
  • 425-12-3456

匹配连字符的示例:

  • --a--b--c
  • 沃尔玛

我们认为我们使用这个 SO question作为参考制定了一个正则表达式来满足后一个标准,但我们不知道如何将它与原始正则表达式结合起来[^a-zA-Z0-9]

由于Lucene 的标准标记器在索引时的工作 方式,我们希望对 Lucene 搜索字符串执行此操作:

在连字符处拆分单词,除非标记中有数字,在这种情况下,整个标记被解释为产品编号并且不被分割。

4

4 回答 4

2

你不能用一个正则表达式来做到这一点。(嗯......也许在 Perl 中。)

编辑:好的,你可以用可变长度的负后视来做到这一点,它似乎Java可以(几乎是独一无二的!)这样做;见Cyborgx37的回答。无论如何,imo,你不应该用一个正则表达式来做到这一点。:))

可以做的是将字符串拆分为单词并单独处理每个单词。我的 Java 非常糟糕,所以这里有一些很有希望的 Python:

# Precompile some regex
looks_like_product_number = re.compile(r'\A[-0-9]+\Z')
not_wordlike = re.compile(r'[^a-zA-Z0-9]')
not_wordlike_or_hyphen = re.compile(r'[^-a-zA-Z0-9]')

# Split on anything that's not a letter, number, or hyphen -- BUT dots
# must be followed by whitespace
words = re.split(r'(?:[^-.a-zA-Z0-9]|[.]\s)+', string)

stripped_words = []
for word in words:
    if '-' in word and not looks_like_product_number.match(word):
        stripped_word = not_wordlike.sub('', word)
    else:
        # Product number; allow dashes
        stripped_word = not_wordlike_or_hyphen.sub('', word)

    stripped_words.append(stripped_word)

pass_to_lucene(' '.join(stripped_words))

当我运行这个时'wal-mart 1-2-3',我回来了'walmart 1-2-3'

但老实说,上面的代码重现了 Lucene 标记器已经在做的大部分事情。我认为您最好将其复制StandardTokenizer到您自己的项目中并对其进行修改以执行您想要的操作。

于 2013-01-18T21:13:45.393 回答
1

你有没有试过这个:

[^a-zA-Z0-9-]

于 2013-01-18T16:46:30.040 回答
1

这个问题很棘手,因为 Java 不允许在环视中进行无限递归,这基本上是您所需要的。正如您将看到的,我已经设置了 100 个字符的限制,如果您希望单词更长,您可以增加该限制。

这应该有效:

(?<![0-9]\S{0,100})[^a-zA-Z](?!\S{0,100}[0-9])|(?<=[0-9]\S{0,100})[^a-zA-Z0-9-](?=\S{0,100}[0-9])

只需一个带有此表达式的简单 replaceAll() 即可处理它。

例如,考虑这个输入:

--9-+-a--7 wal-mart

上面的表达式,其中有问题的字符被替换为长度为零的字符串,将呈现以下输出:

--9--a--7 walmart

你可以在这里试试:http: //fiddle.re/ynyu

请注意,此表达式取决于由空格分隔的单词(空格、制表符、换行符等)。其他字符,例如逗号和分号,将导致表达式将这两个单词视为一个单词。例如 '---9-a-0-,wal-mart' 将被视为一个单词。

编辑我之前编辑的最后一段是不正确的。如果您想包含其他字符作为分隔符,我建议在第一遍中将它们替换为空格(例如,将 ',' 替换为 ' ')。

我主要是一名 .NET 程序员,否则我会为您提供使用此模式的完整 Java 代码。

于 2013-01-18T21:17:19.057 回答
1

请原谅我发布第二个答案而不是编辑第一个答案,但我不完全确定问题是否是在它们立即被字母包围的情况下消除破折号,或者是否打算仅在字符串中消除破折号根本不包含数字。该解决方案适用于后一种情况。我的另一个解决方案是针对前一种情况。

这种模式

String newValue = myString.replaceAll("[^\\sA-Za-z0-9\\-]|((?<!\\S*\\d)-(?!\\S*\\d))", "");

应该这样做。有两个主要部分用or. 第一部分匹配所有非字母、非数字、非破折号的字符,因为无论如何我们都想去掉这些字符。的后半部分or将匹配任何在令牌之前没有数字的破折号,并且在令牌中没有数字(即,令牌中根本没有数字,其中令牌由所有非空格组成,或者\S, 人物)。这是通过消极的后视和前瞻来完成的。我们确实利用了 Java 在这些前瞻/后视中支持可变宽度的事实。当然,替换只是空字符串。

我不得不承认,虽然在 Java 中使用正则表达式的语法很痛苦(在必须使用 Pattern.compile 等的情况下),但至少引擎支持一些不错的特性。尽管根据 Eevee 的说法,它可能不如 .NET 好。

不过,我同意其他人的观点,因为这并不是您通常想要在单个正则表达式中执行的操作。我不知道你的确切情况,但是一个简单的分支来检测它是否看起来是一个产品编号,然后应用正确的模式会更具可读性。

于 2013-01-19T04:13:52.503 回答