0

我有一种奇怪的问题,我试图用一些优雅的正则表达式来解决。

我正在研究的系统最初设计为接受传入的字符串,并通过模式匹配方法更改它然后返回的字符串。一个非常简单的例子是:

传入字符串:

The dog & I went to the park and had a great time...

传出字符串:

The dog {&} I went to the park and had a great time {...}

标点符号映射器包装关键字符或短语,并将它们包装在花括号中。最初的实现是单向的,并不意味着它当前的应用方式,因此,如果它被错误地调用,系统很容易“双重”包装一个字符串,因为它只是在做一个简单的字符串替换。

今天早上我启动了 Regex Hero 并开始研究一些模式匹配,并且将近一年没有写过正则表达式,很快就碰壁了。

我的第一个想法是匹配一个字符(即&),但前提是它没有用大括号括起来并提出[^\{]&[^\}],这很好,但当然可以捕获&符号的任何实例,只要它前面没有花括号,包括空格,并且在有两个与号背靠背的情况下不起作用(即&&需要{&}{&}在传出字符串中。为了使事情更复杂,它并不总是单个字符,因为省略号 ( ...) 也是其中之一映射的值。

我浏览的每个解决方案要么遇到障碍,因为字符串中特定值的出现次数未知,或者捕获组要么过于贪婪,要么最终无法背靠背补偿多个值(即单个周期.vs ellipsis ...),原始开发人员首先通过处理省略号来处理,该省略号涵盖了字符串替换实现中的期间。

是否有任何正则表达式专家对我如何检测字符串中未修饰(未包装)的值,然后以一种也可以处理多个重复字符的不贪婪方式执行它们的替换有任何想法?

我正在处理的数据源是一个简单的键值对,其中包含要搜索的值和要替换它的值。

使用示例字符串更新:

未装饰:

Show Details...   
Default Server:   
"Smart" 2-Way   
Show Lender's Information   
Black & White

装饰:

Show Details{...}
Default Server{:}
{"}Smart{"} 2-Way
Show Lender{'}s Information
Black {&} White

更新了更多具体示例和数据源

数据源(SQL表,可以随时增长):

  • 标记值未标记值

  • {:}:

  • {&} &
  • {<} <
  • {$} $
  • {'}'
  • {} \
  • {>} >
  • {"}"
  • {%} %
  • {...} ...
  • {...} ...</li>
  • {:}:</li>
  • {"}"</li>
  • {“}”</li>
  • {'} `
  • {'}'</li>

断弦: This is a string that already has stuff {&} other stuff{!} and {...} with {_} and {@} as well{.} and here are the same characters without it & follow by ! and ... _ & . &&&

需要装饰的字符串: Show Details... Default Server: "Smart" 2-Way Show Lender's Information Black & White

将通过方法不变的字符串(因为它已经被装饰): The dog {&} I went to the park and had a great time {...}

转向正则表达式的另一个“陷阱”是需要处理转义,尤其是优雅的反斜杠,因为它们在正则表达式中的功能。

使用@Ethan Brown 的输出更新

@伊桑布朗,

我开始认为正则表达式,虽然优雅可能不是这里的方式。您提供的更新代码虽然更接近,但仍不能产生正确的结果,并且所涉及的变量数量可能超出正则表达式逻辑的能力。

使用我上面的例子:

'This is a string that already has stuff {&} other stuff{!} and {...} with {_} and {@} as well{.} and here are the same characters without it & follow by ! and ... _ & . &&&'

产量

This is a string that already has stuff {&} other stuff{!} and {...} with {_} and {@} as well{.} and here are the same characters without it {&} follow by {!} and {...} {_} {&} . {&&}&

最后一组应该以 {&}{&}{&} 形式出现的 & 符号实际上以 {&&}& 形式出现。

这里有很多可变性(即需要处理来自远东语言的省略号和宽省略号),并且需要使用数据库作为数据源是最重要的。

我想我只是要编写一个自定义评估器,我可以很容易地编写它来执行这种类型的验证并暂时搁置正则表达式路线。一旦我进入桌面浏览器,我就会为您的回答和工作给予肯定。

4

3 回答 3

1

这种问题可能真的很棘手,但让我给你一些可能会有所帮助的想法。真正让您头疼的一件事是处理标点符号出现在字符串开头或结尾的情况。当然,在正则表达式中可以使用类似 的构造来处理(^|[^{])&($|[^}]),但除了难以阅读之外,它还存在效率问题。但是,有一种简单的方法可以“作弊”并解决这个问题:只需在输入字符串的两端用空格填充:

var input = " " + originalInput + " ";

完成后,您可以修剪。当然,如果您关心在开头或结尾保留输入,则必须更聪明,但为了论证的缘故,我假设您不这样做。

所以现在开始讨论问题的实质。当然,我们可以想出一些复杂的正则表达式来做我们正在寻找的事情,但如果你使用多个正则表达式,答案通常要简单得多。

由于您已使用更多字符和更多问题输入更新了答案,因此我已更新此答案以使其更加灵活:希望随着更多字符的添加,它将更好地满足您的需求。

查看您的输入空间,以及您需要引用的表达式,实际上有三种情况:

  • 单字符替换(例如!变成 {!})。
  • 多字符替换(... 变为 {...})。
  • 斜线替换(\ 变为 {})

由于句点包含在单字符替换中,因此顺序很重要:如果先替换所有句点,则会错过省略号。

因为我发现 C# 正则表达式库有点笨拙,所以我使用以下扩展方法使其更加“流畅”:

public static class StringExtensions {
    public static string RegexReplace( this string s, string regex, string replacement ) {
        return Regex.Replace( s, regex, replacement );
    }
}

现在我可以涵盖所有情况:

// putting this into a const will make it easier to add new
// characters in the future
const string normalQuotedChars = @"\!_\\:&<\$'>""%:`";

var output = s
    .RegexReplace( "(?<=[^{])\\.\\.\\.(?=[^}])", "{$&}" )
    .RegexReplace( "(?<=[^{])[" + normalQuotedChars + "](?=[^}])", "{$&}" )
    .RegexReplace( "\\\\", "{}" );

所以让我们分解这个解决方案:

  1. 首先,我们处理省略号(这将防止我们在以后遇到问题)。请注意,我们在表达式的开头和结尾使用零宽度断言来排除已引用的表达式。零宽度断言是必要的,因为没有它们,我们会因为引号字符彼此相邻而遇到麻烦。例如,如果您有正则表达式([^{])!([^}]),而您的输入字符串是foo !! bar,则匹配将包括第一个感叹号和第二个感叹号之前的空格。因此,天真的替换$1!$2将产生foo {!}! bar因为第二个感叹号会作为比赛的一部分被消耗掉。您最终必须进行详尽的匹配,并且仅使用不消耗的零宽度断言要容易得多。

  2. 然后我们处理所有正常的引用字符。请注意,出于与上述相同的原因,我们在这里使用零宽度断言。

  3. 最后,我们可以找到单独的斜杠(注意我们必须将其转义两次:一次用于 C# 字符串,一次用于正则表达式元字符)并将其替换为空大括号。

我通过这一系列匹配运行了您所有的测试用例(以及我自己的一些发明),并且一切都按预期工作。

于 2013-09-09T16:26:42.667 回答
0

忽略原始输入字符串具有{or}字符的情况,避免将正则表达式重新应用于已经转义的字符串的常用方法是查找转义序列并将其从字符串中删除,然后再将正则表达式应用于余数。这是一个示例正则表达式,用于查找已经转义的内容:

Regex escapedPattern = new Regex(@"\{[^{}]*\}"); // consider adding RegexOptions.Compiled

这种否定字符类模式的基本思想来自regular-expressions.info,这是一个对所有正则表达式都非常有用的网站。该模式有效,因为对于任何最内层的大括号,必须有 a{后跟 non {},后跟 a}

在输入字符串上运行escapedPattern,查找每个Match获取原始字符串中的开始和结束索引并将它们子串出来,然后使用最终清理的字符串再次运行原始模式匹配或使用类似以下内容:

Regex punctPattern = new Regex(@"[^\w\d\s]+"); // this assumes all non-word, 
      // digit or space chars are punctuation, which may not be a correct 
      //assumption

Match.Groups[1].Value"{" + Match.Groups[1].Value + "}"

于 2013-09-09T16:17:29.580 回答
0

我不是正则表达式之神,所以一种简单的方法:

  • 获取/构造最终的替换字符串 - 例如。“{...}”、“{&}”
  • 将输入中出现的所有这些替换为保留的字符(救援的 unicode)
  • 运行匹配的正则表达式并放置“{”或任何所需的标记。
  • 将保留的字符替换为原始字符串。
于 2013-09-09T16:01:57.857 回答