如何从文本文件中提取数据

4

我需要解析一些大的文本文件,并提取显示名称区号,仅当它与以下模式匹配时:

  • 行以显示名称开头(任意数量的单词,但不能包含数字或特殊字符)
  • 后跟6个数字(可以包含空格)
  • 后跟#text标记

输入文件

John doe 123 456 #text some text
Test 123456 #text
Test$test 123456 #text
Test123 345678 #text
Test 123 #test
Test 123456 #test1
Test 123g45 #test

输入和期望输出

John doe 123 456 #text some text
Display name: John doe
Area code: 123 456

Test 123456 #text
Display name: Test
Area code: 123456

Test$test 123456 #text
Invalid, display name contains special character

Test123 345678 #text
Invalid, display name contains digits

Test 123 #test
Invalid, area code contains only 3 digits

Test 123456 #test1
Invalid, contains invalid tag

Test 123g45 #test
Invalid, area code contains letters

我知道如何打开文本文件并逐行读取,但是在编写正则表达式时遇到了困难。

以下是我尝试过的内容:

private static void Main(string[] args)
{
    string text = "John Doe 123 45 #text Lorem ipsum dolor :)";
    string pattern = @"(\w+)*([0-9]{2,5}).([0-9]{2,5}).#text";
    Match match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);

    if (match.Success)
    {
        string key = match.Groups[0].Value;
        Console.WriteLine(key);
    }
}

编辑:

下面有更详细的解释。

显示名称
显示名称可以包含任意数量的单词,例如John Michael Smith是有效的,因为John是名字,Michael是中间名,Smith是姓氏。 Šljaker也是有效的显示名称,因为它是某人的昵称,可能包含非英语字符。但是带数字的名称无效,例如John1。为什么?这是我们的业务规则,不能有数字:)我想\w在这里会起作用,而a-zA-Z不会,因为它不包括非英语字母。

区号
业务规则很简单:它必须包含6个数字,我们不关心它们的格式。 所有这些都是有效的区号:123456、12 34 56、1234 56等。正则表达式不需要修剪空格,我会在代码中处理它们。

非常感谢您的帮助!


@Superbest,我已经编辑了我的问题,但是很明显我的正则表达式非常错误:( - šljaker
哦,我假设你的文本文件只由这种行组成:John doe 123 456 #text some text,Test 123456 #test1? - berty
@aurel.g,是的,没错。在示例中,我展示了这行代码可能看起来像什么,期望的输出是什么,或者为什么这行代码包含无效数据。 - šljaker
@šljaker:关于显示名称,我们能做出什么假设? - nhahtdh
@nhahtdh,任意数量的单词,不能包含数字或特殊字符。因此,“nhahtdh”是有效的,“John Doe”也是有效的,但“nhahtdh123”无效,因为它包含“123”。 - šljaker
显示剩余4条评论
2个回答

0

输入规范仍然有些模糊,无法编写适当的正则表达式。在编写正则表达式之前,需要先明确规范,或者至少做出适用于当前数据集的假设。

这是我编写的正则表达式,该表达式将验证整行文本。假设稍后再进行。

^([a-zA-Z ]+)(?<! ) +((?:[0-9] *){6}) *#text( .*)?$

输入中只允许空格(ASCII 32)。如果您想允许制表符和其他空白字符,请在正则表达式中使用\s替换空格。

([a-zA-Z ]+)(?<! ) +:匹配并捕获显示名称。显示名称中只允许使用英文字母和空格。 (?<! )用于抑制捕获组中的尾随空格;它还有另一个效果,即不允许仅包含空格的名称。如果您想允许匹配Unicode中的任何字母,请使用[\p{L} ]+代替[a-zA-Z ]+

((?:[0-9] *){6}):恰好由任意数量的空格分隔的6个十进制数字。由于匹配了1个或多个空格 +,它将不允许数字粘在显示名称上。

*#text( .*)?:我允许数字与标签粘在一起(例如Name 123456#text dfsjhdf);将其更改为+#text( .*)?以禁止此操作。如果有更多注释或没有跟随#text,则(.*)?确保在#text之后有一个空格(例如Name 123456 #text)。

从此正则表达式捕获的文本将:

  • 显示名称:可能包含过多的前导空格和间隔,但不包含尾随空格。至少有一个英文字母。
  • 区号:恰好包含6个数字。其中间可能有任意空格,因此您可能需要处理匹配的字符串。

非常感谢!您的“正则表达式”适用于此文本“John Doe Smith 12 3456 #text3 Test”,即使它包含“text3”标签而不仅仅是“text”标签。 - šljaker

0
你可以用以下内容替换正则表达式模式:
@"^([\D]+)\s*((?:\d\s*?){6})\s*\#text\s*(.*)$"

这个正则表达式按顺序搜索:

  • 一系列非数字字符(第一组)
  • 后面跟任意数量的空格
  • 后面跟着恰好6个数字,每个数字后面可以选择性地跟着空格(第二组)
  • 后面跟任意数量的空格和字面上的“#text”,后面可以选择性地跟着空格
  • 剩余部分为(第三组)

由于这将针对许多行进行评估,请考虑添加RegexOptions.Compiled标志(即:RegexOptions.IgnoreCase | RegexOptions.Compiled)。

显示名称存储在match.Groups[1].Value中;区号存储在match.Groups[2].Value中,文本存储在match.Groups[3].Value中。


谢谢!你的正则表达式对于这段文字John Doe Smith 12 3456 #text3 Test返回了匹配,尽管它包含了text3标签而不仅仅是text标签。 - šljaker
正则表达式将允许无效的情况,例如 ;;;;; 123456 #text - nhahtdh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接