正则表达式匹配特殊字符EXCEPT连字符与数字混合

时间:2013-01-18 16:44:23

标签: java regex

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

连字符不会匹配的示例:

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

匹配 的示例:

  • - A - B - ç
  • 沃尔玛

我们认为我们使用this SO question作为参考,制定了一个符合后一条件的正则表达式,但我们不知道如何将其与原始正则表达式[^a-zA-Z0-9]结合起来。

由于Lucene's standard tokenizer在编制索引时的工作方式,我们希望对Lucene搜索字符串执行此操作:

  

使用连字符拆分单词,除非令牌中有数字,在这种情况下,整个令牌被解释为产品编号而不会被拆分。

4 个答案:

答案 0 :(得分:2)

单个正则表达式无法做到这一点。 (好吧......也许在Perl中。)

编辑:好的,你可以使用可变长度负面的lookbehind,看起来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 tokenizer已经在做的大部分内容。我认为你最好只是将StandardTokenizer复制到你自己的项目中并修改它以做你想做的事。

答案 1 :(得分:1)

你试过这个:

[^a-zA-Z0-9-]

答案 2 :(得分: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-,沃尔玛'将被视为一个单词。

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

我主要是.NET程序员,否则我会给你使用这个模式的完整Java代码。

答案 3 :(得分:1)

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

此模式

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

应该这样做。有两个主要部分与or连接在一起。第一部分匹配所有非alpha,非数字,非破折号字符,因为我们想要将这些字符剥离出去,无论如何。 or的后半部分将匹配令牌中前面没有任何数字的任何破折号,并且在令牌中没有任何数字(即,令牌中根本没有数字,其中令牌由所有代码组成)非空格,或\S,字符)。这是通过负面的后视和前瞻来实现的。我们利用了Java在这些前瞻/后方支持可变宽度这一事实。当然,替换只是空字符串。

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

我同意其他人的意见,因为这不是你通常想要在一个正则表达式中做的事情。我不知道你的具体情况,但是一个简单的分支来检测它是否是一个产品编号,然后应用正确的模式将更具可读性。