仅当未满足条件时才匹配正则表达式

本文关键字:正则表达式 未满足 条件 | 更新日期: 2023-09-27 18:30:51

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

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

传入字符串:

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

传出字符串:

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

标点映射器将关键字符或短语包装起来,并用大括号括起来。最初的实现是单行道,从来都不是针对它当前应用的方式,因此,如果调用不正确,系统很容易"双重"包装字符串,因为它只是做一个简单的字符串替换。

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

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

我点头的每个解决方案要么遇到障碍,因为字符串中特定值的出现次数未知,要么捕获组要么过于贪婪,要么最终无法连续补偿多个值(即单个句点.与省略号 ... ),原始开发人员通过首先处理省略号来处理,该省略号覆盖了字符串替换实现中的时间段。

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

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

使用示例字符串进行了更新:

未装饰:

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>
  • {$} $
  • {'} '
  • {} ''
  • {>}>
  • {"} "
  • {%} %
  • {...} ...
  • {...} 。
  • {:} :
  • {"} "
  • {"} "
  • {'} '
  • {'} '

断线: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布朗的输出

@Ethan布朗,

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

使用我上面的例子:

'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 {...} {_} {&} . {&&}&

其中,最后一组应该显示为 {&}{&}{&}{&} 的 & 符号实际上显示为 {&&}}。

这里有太多的可变性(即需要处理远东语言中的省略号和宽省略号),并且需要利用数据库作为数据源至关重要。

我想我只是要编写一个自定义评估器,我可以轻松编写它来执行这种类型的验证并暂时搁置正则表达式路由。我会给你的答案,并在我到达桌面浏览器前后立即工作。

仅当未满足条件时才匹配正则表达式

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

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# 字符串,另一次用于正则表达式元字符),并将其替换为空的大括号。

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

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

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

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

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

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

对输入字符串运行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(组是基于 0 的数组,其中 0 是整个匹配项,1 是第一组括号,2 是下一组等)替换为 "{" + Match.Groups[1].Value + "}"