正则表达式仅匹配一次出现的关键字
RegEx to only match single occurence of a keyword
我很难尝试编写 RegEx 来满足我的特定要求。
这些是:
- 匹配关键字并捕获后面的日期
- 如果关键字不存在,则什么也不捕获
- 如果关键字多次出现,则不捕获任何内容
关键词:
LT circa
示例文本:
Metall-Notierung 464,95 EUR 100 KG
* LT circa 21.04.2020 2 x 500 M Einwegtrommel 400x 150x 404mm
* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm
Zolltarifnummer 80464995
预期结果:
NULL
示例文本:
Metall-Notierung 464,95 EUR 100 KG
* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm
Zolltarifnummer 80464995
预期结果:
17.05.2020
作为正则表达式的新手,这些是我迄今为止在简化主题上尝试过的事情:
This test is a test and nothing else
(.*test.*test.*)?(?(1)(a^):(test.*))
...如您所料,如果认为这可行就太天真了。
有专家吗?
编辑:
我使用 .NET Framework 4.7.2 和 NUnit 进行了检查
using NUnit.Framework;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
namespace Test.RegExpressions.Tests
{
[TestFixture]
public class SpecialRegexTests
{
[TestCaseSource(typeof(TestCaseClass), nameof(TestCaseClass.TestCases))]
public int MatchTest(string input, string pattern, RegexOptions regexOptions)
{
return new Regex(pattern, regexOptions).Matches(input).Count;
}
}
public static class TestCaseClass
{
private static readonly string S0 = new StringBuilder()
.AppendLine("Metall - Notierung 464,95 EUR 100 KG")
.AppendLine("* LT circa 21.04.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("Zolltarifnummer 80464995")
.ToString();
private static readonly string S1 = new StringBuilder()
.AppendLine("Metall - Notierung 464,95 EUR 100 KG")
.AppendLine("* LxT circa 21.04.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("Zolltarifnummer 80464995")
.ToString();
private const string R0 = @"^(?:(?!.*LT circa).+\n)*(?:(?!LT circa).)*LT circa\s+(\d\d\.\d\d.\d{4})(?!(?:.+\n)*.*LT circa)";
private const string R1 = @"(?s)^(?!(?:.*LT circa){2}).*LT circa\s*\K\d{1,2}\.\d{1,2}\.\d{4}";
private const string R2 = @"(?s)^(?!(?:.*LT circa){2}).*LT circa\s*(\d{1,2}\.\d{1,2}\.\d{4})";
public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(S0, R0, RegexOptions.None).Returns(0);
yield return new TestCaseData(S1, R0, RegexOptions.None).Returns(1);
yield return new TestCaseData(S0, R1, RegexOptions.None).Returns(0);
yield return new TestCaseData(S1, R1, RegexOptions.None).Returns(1);
yield return new TestCaseData(S0, R2, RegexOptions.None).Returns(0);
yield return new TestCaseData(S1, R2, RegexOptions.None).Returns(1);
}
}
}
}
除了使用\K
的R1
外,全部通过测试
一旦我获得有关正在使用的 Regex Flavor 的更多信息,我将立即更新我的问题。
值得一提的是,其中 none 个在软件中有效,这可能是也可能不是我无法控制的 RegEx 选项的问题。
您可以使用
(?s)(?<=^(?!(?:.*LT circa){2}).*LT circa\s*)\d{1,2}\.\d{1,2}\.\d{4}
见regex demo。日期正则表达式可以增强,但要点是它周围的模式。
详情:
(?s)
- s
flag making .
匹配任意字符
(?<=
- 正面回顾的开始:
^
- 字符串开头
(?!(?:.*LT circa){2})
- 如果 LT circa
在字符串 中的任意位置出现两次,则匹配失败
.*
- 尽可能多的任意零个或多个字符
LT circa
- 关键字
\s*
- 零个或多个空格
)
- 正面回顾结束
\d{1,2}\.\d{1,2}\.\d{4}
- 日期模式。 (?:0?[1-9]|[12]\d|3[01])\.(?:0?[1-9]|1[0-2])\.\d{4}(?!\d)
可以是任意 dd/MM/yyyy 日期(不支持闰年)的更精确的模式。
您可以用负数 look-aheads 试试这个正则表达式。它稍长但会比使用 DOTALL
模式更有效:
^(?:(?!.*LT circa).+\n)*(?:(?!LT circa).)*LT circa\s+(\d\d\.\d\d.\d{4})(?!(?:.+\n)*.*LT circa)
我很难尝试编写 RegEx 来满足我的特定要求。
这些是:
- 匹配关键字并捕获后面的日期
- 如果关键字不存在,则什么也不捕获
- 如果关键字多次出现,则不捕获任何内容
关键词:
LT circa
示例文本:
Metall-Notierung 464,95 EUR 100 KG
* LT circa 21.04.2020 2 x 500 M Einwegtrommel 400x 150x 404mm
* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm
Zolltarifnummer 80464995
预期结果:
NULL
示例文本:
Metall-Notierung 464,95 EUR 100 KG
* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm
Zolltarifnummer 80464995
预期结果:
17.05.2020
作为正则表达式的新手,这些是我迄今为止在简化主题上尝试过的事情:
This test is a test and nothing else
(.*test.*test.*)?(?(1)(a^):(test.*))
...如您所料,如果认为这可行就太天真了。
有专家吗?
编辑:
我使用 .NET Framework 4.7.2 和 NUnit 进行了检查
using NUnit.Framework;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
namespace Test.RegExpressions.Tests
{
[TestFixture]
public class SpecialRegexTests
{
[TestCaseSource(typeof(TestCaseClass), nameof(TestCaseClass.TestCases))]
public int MatchTest(string input, string pattern, RegexOptions regexOptions)
{
return new Regex(pattern, regexOptions).Matches(input).Count;
}
}
public static class TestCaseClass
{
private static readonly string S0 = new StringBuilder()
.AppendLine("Metall - Notierung 464,95 EUR 100 KG")
.AppendLine("* LT circa 21.04.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("Zolltarifnummer 80464995")
.ToString();
private static readonly string S1 = new StringBuilder()
.AppendLine("Metall - Notierung 464,95 EUR 100 KG")
.AppendLine("* LxT circa 21.04.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("* LT circa 17.05.2020 2 x 500 M Einwegtrommel 400x 150x 404mm")
.AppendLine("Zolltarifnummer 80464995")
.ToString();
private const string R0 = @"^(?:(?!.*LT circa).+\n)*(?:(?!LT circa).)*LT circa\s+(\d\d\.\d\d.\d{4})(?!(?:.+\n)*.*LT circa)";
private const string R1 = @"(?s)^(?!(?:.*LT circa){2}).*LT circa\s*\K\d{1,2}\.\d{1,2}\.\d{4}";
private const string R2 = @"(?s)^(?!(?:.*LT circa){2}).*LT circa\s*(\d{1,2}\.\d{1,2}\.\d{4})";
public static IEnumerable TestCases
{
get
{
yield return new TestCaseData(S0, R0, RegexOptions.None).Returns(0);
yield return new TestCaseData(S1, R0, RegexOptions.None).Returns(1);
yield return new TestCaseData(S0, R1, RegexOptions.None).Returns(0);
yield return new TestCaseData(S1, R1, RegexOptions.None).Returns(1);
yield return new TestCaseData(S0, R2, RegexOptions.None).Returns(0);
yield return new TestCaseData(S1, R2, RegexOptions.None).Returns(1);
}
}
}
}
除了使用\K
的R1
外,全部通过测试
一旦我获得有关正在使用的 Regex Flavor 的更多信息,我将立即更新我的问题。
值得一提的是,其中 none 个在软件中有效,这可能是也可能不是我无法控制的 RegEx 选项的问题。
您可以使用
(?s)(?<=^(?!(?:.*LT circa){2}).*LT circa\s*)\d{1,2}\.\d{1,2}\.\d{4}
见regex demo。日期正则表达式可以增强,但要点是它周围的模式。
详情:
(?s)
-s
flag making.
匹配任意字符(?<=
- 正面回顾的开始:^
- 字符串开头(?!(?:.*LT circa){2})
- 如果LT circa
在字符串 中的任意位置出现两次,则匹配失败
.*
- 尽可能多的任意零个或多个字符LT circa
- 关键字\s*
- 零个或多个空格
)
- 正面回顾结束\d{1,2}\.\d{1,2}\.\d{4}
- 日期模式。(?:0?[1-9]|[12]\d|3[01])\.(?:0?[1-9]|1[0-2])\.\d{4}(?!\d)
可以是任意 dd/MM/yyyy 日期(不支持闰年)的更精确的模式。
您可以用负数 look-aheads 试试这个正则表达式。它稍长但会比使用 DOTALL
模式更有效:
^(?:(?!.*LT circa).+\n)*(?:(?!LT circa).)*LT circa\s+(\d\d\.\d\d.\d{4})(?!(?:.+\n)*.*LT circa)