【问题标题】:Recursive RegEx to match keys and name递归正则表达式以匹配键和名称
【发布时间】:2013-04-10 16:04:44
【问题描述】:

我有字符串,["02-03-2013#3rd Party Fuel", "-1#Archived", "2#06-23-2013#Newswire"],我想将其分解为几个部分。这些字符串以日期和索引键为前缀并包含名称。

我设计了一个RegEx,可以正确匹配每个键。但是,如果我想一举匹配索引键、日期键和名称。只找到第一个键。递归组似乎没有像我预期的那样工作。

private const string INDEX_KEY_REGEX = @"(?<index>-?\d+)";
private const string DATE_KEY_REGEX = @"(?<date>(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|3[01])-\d{4})";
private const string KEY_SEARCH_REGEX = @"(?<R>(?:^|(?<=#))({0})#(?(R)))(?<name>.*)";

private string Name = "2#06-23-2013#Newswire"
... = Regex.Replace(
    Name,
    String.Format(KEY_SEARCH_REGEX, INDEX_KEY_REGEX + "|" + DATE_KEY_REGEX),
    "${index}, ${date}, ${name}"
);

// These are the current results for all strings when set into the Name variable.

// Correct Result: ", 02-03-2013, 3rd Party Fuel"
// Correct Result: "-1, , Archived"
// Invalid Result: "2, , 06-23-2013#Newswire"
// Should be: "2, 06-23-2013, Newswire"

敏锐的眼睛能看到我错过的东西吗?


我需要的最终解决方案

事实证明我不需要递归组。我只需要 0 到多个序列。这是完整的RegEx

(?:(?:^|(?<=#))(?:(?<index>-?\d+)|(?<date>(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|3[01])-(\d{2}|\d{4})))#)*(?<name>.*)

而且,分段的RegEx

private const string INDEX_REGEX = @"(?<index>-?\d+)";
private const string DATE_REGEX = @"(?<date>(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|3[01])-(\d{2}|\d{4}))";
private const string KEY_WRAPPER_REGEX = @"(?:^|(?<=#))(?:{0})#";
private const string KEY_SEARCH_REGEX = @"(?:{0})*(?<name>.*)";

【问题讨论】:

  • 如果您删除 ^| 测试,它适用于此测试用例。这样做的目的是什么?
  • 那是为了确保键总是在开头而不是结尾,或者在它们之前有一个被破坏的字符串。我会在没有它的情况下进行测试,但我认为它会违反规定的要求。我会看看我能找到什么。
  • 所以通过我的测试得到以下结果2#, 06-23-2013, Newswire-1#Archived(不匹配)。所以这与期望的相差甚远。
  • 所有三个测试的期望结果是什么?我只知道一个。
  • 我已将当前结果添加到问题中。

标签: c# regex recursive-regex


【解决方案1】:

好吧,各个正则表达式分解为:

索引:捕获单个正数或负数。 (-、0 或 1 个代表,后跟一位或多位数字)

date:指定日期字符串,用-分隔。不考虑任何其他日期格式。注意,不处理前导“#”和尾随“#”,它专门捕获日期,并且只捕获日期

R:行的开头 OR #,然后是格式替换,使其成为一个 BIG 正则表达式...然后是另一个 #,指定。那么一个没有 false 的条件......并且 true 也不做任何事情。

name:捕获剩下的任何东西。

最终结果,编译成一个正则表达式...... 两个捕获:R 和名称。 R:(4部分) R-1:匹配行首或# R-2:获取(但不能同时获取两者)日期或索引 R-3:匹配# R-4:空条件表达式 name: 匹配剩下的。

问题似乎是您没有同时匹配索引和日期

最终编辑,工作正则表达式

请耐心等待,这东西很讨厌。您必须考虑所有 4 种可能性,否则它不会匹配所有可能的情况。我想不出任何方法来概括它。

(?:(?<index>-?\d+(?!\d-))#(?<date>(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|:3[01])-\d{4})|(?<date>(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|:3[01])-\d{4})#(?<index>-?\d+)|(?!-?\d+#)(?<date>(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|:3[01])-\d{4})|(?<index>-?\d+)(?!#(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|:3[01])-\d{4}))#(?<name>.*)

丑,我知道。它有 4 个初始条件。

1a) capture <index>#<date>  OR
1b) capture <date>#<index>  OR
1c) capture <index> only, as long as its not followed by a date  OR
1d) capture <date> only, as long as its not preceded by an index
...
2) match but ignore #
3) capture <name>

适用于所有 4 种情况。

最终:最终编辑

有一种方法可以使用 3 个正则表达式而不是 1 个正则表达式,这最终可能会更简洁。

//note: index MIGHT be preceeded by, and is ALWAYS followed by, a #
indexRegex = @"((?=#)?(?<!\d|-)-?\d+(?=#))";
//same with date
dateRegex = @"((?=#)?(?:0?[1-9]|1[012])-(?:0?[1-9]|[12]\d|3[01])-\d{4}(?=#))";
//then name
nameRegex = @"(?:.*#){1,2}(.*)";

分别针对替换运行它们以获取各个变量,然后重建字符串。

【讨论】:

  • 这看起来基本正确,但INDEX_KEY_REGEX 中的+ 并不表示正则表达式将匹配至少一个或多个\d
  • 目前,这仅适用于单个数字索引,并且可能会弄乱日期......仍在努力。
  • 这与需要的非常接近,但是,它不考虑日期键可能在索引键06-23-2013#2#Newswire 之前的情况。这就是我想尝试递归语句的原因。你会怎么处理这个?很抱歉没有早点解释。
  • @roydukkey - 你的数据。但这就是正则表达式的用途。
  • @Bobson 说实话,我仍然很生气,因为我找不到更短的方法,那东西很丑。如果其他人能找到缩小它的方法,请随意。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-30
  • 2016-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-11
  • 2011-05-23
相关资源
最近更新 更多