C# 正则表达式:轻松提取匹配项并按行显示
正则表达式,作为一种强大的文本处理工具,在C#开发中扮演着至关重要的角色。它允许我们通过定义模式来搜索、验证、提取和替换字符串中的特定内容。本文将深入探讨C#中正则表达式的用法,重点讲解如何提取匹配项并按行显示,并通过丰富的示例代码和详尽的解释,帮助读者掌握这一实用技巧。
一、C# 正则表达式基础
在C#中使用正则表达式,主要依赖于 System.Text.RegularExpressions
命名空间下的 Regex
类。理解 Regex
类的常用方法和选项是使用正则表达式的基础。
1. Regex
类常用方法
Match(string input)
: 在输入字符串中搜索第一个匹配项。返回Match
对象,包含匹配结果的信息。Matches(string input)
: 在输入字符串中搜索所有匹配项。返回MatchCollection
对象,包含所有Match
对象的集合。IsMatch(string input)
: 判断输入字符串中是否存在匹配项。返回布尔值,表示是否匹配。Replace(string input, string replacement)
: 替换输入字符串中所有匹配项为指定的字符串。返回替换后的字符串。Split(string input)
: 根据正则表达式将输入字符串分割成字符串数组。返回字符串数组。Escape(string str)
: 转义字符串中的特殊字符,使其可以作为正则表达式中的字面量使用。Unescape(string str)
: 取消转义字符串中的转义字符。
2. 正则表达式语法
掌握正则表达式的语法是至关重要的。以下是一些常用的正则表达式元字符和符号:
.
(点): 匹配除换行符之外的任何单个字符。^
(脱字符): 匹配字符串的开头。$
(美元符号): 匹配字符串的结尾。[]
(字符集): 匹配字符集中的任何单个字符。例如,[abc]
匹配 ‘a’、’b’ 或 ‘c’。[^]
(否定字符集): 匹配不在字符集中的任何单个字符。例如,[^abc]
匹配除 ‘a’、’b’ 或 ‘c’ 之外的任何字符。*
(星号): 匹配前面的字符零次或多次。+
(加号): 匹配前面的字符一次或多次。?
(问号): 匹配前面的字符零次或一次。{n}
(量词): 匹配前面的字符恰好 n 次。例如,a{3}
匹配 “aaa”。{n,}
(量词): 匹配前面的字符至少 n 次。例如,a{2,}
匹配 “aa”、”aaa”、”aaaa” 等。{n,m}
(量词): 匹配前面的字符至少 n 次,但不超过 m 次。例如,a{2,4}
匹配 “aa”、”aaa” 或 “aaaa”。|
(管道符): 表示“或”的关系,匹配管道符前或后的表达式。例如,a|b
匹配 ‘a’ 或 ‘b’。()
(分组): 将表达式分组,可以捕获匹配的子字符串。\d
: 匹配任何数字字符 (0-9)。\D
: 匹配任何非数字字符。\w
: 匹配任何单词字符 (a-z, A-Z, 0-9, _)。\W
: 匹配任何非单词字符。\s
: 匹配任何空白字符 (空格、制表符、换行符等)。\S
: 匹配任何非空白字符。
3. RegexOptions
枚举
RegexOptions
枚举提供了一组标志,可以修改正则表达式引擎的行为。一些常用的选项包括:
IgnoreCase
: 忽略大小写。Multiline
: 将^
和$
分别匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。Singleline
: 使.
匹配任何字符,包括换行符。Compiled
: 将正则表达式编译为可重用的程序集,提高匹配速度。ExplicitCapture
: 只捕获显式命名的组。
二、提取匹配项
提取匹配项是正则表达式最常用的功能之一。Match
和 MatchCollection
对象提供了访问匹配项信息的途径。
1. 使用 Match
对象提取单个匹配项
当使用 Regex.Match()
方法时,会返回一个 Match
对象。 该对象包含以下重要属性:
Success
: 布尔值,指示是否找到了匹配项。Value
: 包含匹配的字符串。Index
: 匹配项在输入字符串中的起始位置。Length
: 匹配项的长度。Groups
: 包含捕获组的集合。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = “Hello, my email is [email protected]. Contact me!”;
string pattern = @”\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*”; // 匹配电子邮件地址
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine("匹配成功!");
Console.WriteLine("匹配的电子邮件地址: " + match.Value);
Console.WriteLine("起始位置: " + match.Index);
Console.WriteLine("长度: " + match.Length);
}
else
{
Console.WriteLine("未找到匹配项!");
}
}
}
“`
2. 使用 MatchCollection
对象提取多个匹配项
当需要提取所有匹配项时,可以使用 Regex.Matches()
方法,该方法返回一个 MatchCollection
对象。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = “Hello, my email is [email protected]. Another email is [email protected]. Contact me!”;
string pattern = @”\w+([-+.]\w+)@\w+([-.]\w+).\w+([-.]\w+)*”; // 匹配电子邮件地址
MatchCollection matches = Regex.Matches(input, pattern);
if (matches.Count > 0)
{
Console.WriteLine("找到 " + matches.Count + " 个电子邮件地址:");
foreach (Match match in matches)
{
Console.WriteLine(" - " + match.Value);
}
}
else
{
Console.WriteLine("未找到匹配项!");
}
}
}
“`
3. 提取捕获组
正则表达式中的括号 ()
定义了捕获组。可以使用 Match.Groups
属性访问捕获组。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = “My phone number is (123) 456-7890.”;
string pattern = @”((\d{3}))\s(\d{3})-(\d{4})”; // 匹配美国电话号码格式,并捕获区号、前缀和号码
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine("匹配成功!");
Console.WriteLine("完整号码: " + match.Value);
Console.WriteLine("区号: " + match.Groups[1].Value);
Console.WriteLine("前缀: " + match.Groups[2].Value);
Console.WriteLine("号码: " + match.Groups[3].Value);
}
else
{
Console.WriteLine("未找到匹配项!");
}
}
}
“`
三、按行显示匹配项
很多时候,我们需要处理多行文本,并按行显示匹配项。 这需要结合正则表达式的 Multiline
选项和字符串的行分割方法。
1. 使用 Multiline
选项
Multiline
选项使 ^
和 $
分别匹配每一行的开头和结尾,而不是整个字符串的开头和结尾。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = @”Line 1: This line contains the word ‘example’.
Line 2: Another line with ‘example’ in it.
Line 3: No example here.
Line 4: The word ‘example’ appears again.”;
string pattern = @"^.*example.*$"; // 匹配包含 "example" 的整行
Regex regex = new Regex(pattern, RegexOptions.Multiline);
MatchCollection matches = regex.Matches(input);
if (matches.Count > 0)
{
Console.WriteLine("包含 'example' 的行:");
foreach (Match match in matches)
{
Console.WriteLine(" - " + match.Value);
}
}
else
{
Console.WriteLine("未找到匹配项!");
}
}
}
“`
2. 按行分割字符串并逐行匹配
另一种方法是将字符串按行分割成字符串数组,然后逐行应用正则表达式。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = @”Line 1: This line contains the word ‘example’.
Line 2: Another line with ‘example’ in it.
Line 3: No example here.
Line 4: The word ‘example’ appears again.”;
string pattern = @"example"; // 匹配 "example"
string[] lines = input.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
Console.WriteLine("包含 'example' 的行:");
foreach (string line in lines)
{
if (Regex.IsMatch(line, pattern))
{
Console.WriteLine(" - " + line);
}
}
}
}
“`
四、高级用法和技巧
1. 命名捕获组
可以为捕获组命名,使其更易于理解和使用。 使用 (?<name>...)
语法来命名捕获组。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = “My phone number is (123) 456-7890.”;
string pattern = @”((?
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine("匹配成功!");
Console.WriteLine("区号: " + match.Groups["AreaCode"].Value);
Console.WriteLine("前缀: " + match.Groups["Prefix"].Value);
Console.WriteLine("号码: " + match.Groups["Number"].Value);
}
else
{
Console.WriteLine("未找到匹配项!");
}
}
}
“`
2. 零宽度断言
零宽度断言允许在不消耗字符的情况下匹配字符串中的位置。常用的零宽度断言包括:
(?=...)
(正向先行断言): 断言当前位置的后面必须匹配指定的模式。(?!...)
(负向先行断言): 断言当前位置的后面不能匹配指定的模式。(?<=...)
(正向后行断言): 断言当前位置的前面必须匹配指定的模式。(?<!...)
(负向后行断言): 断言当前位置的前面不能匹配指定的模式。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = “The price is $100, but the discount is $20.”;
string pattern = @”(?<=\$)\d+”; // 匹配以 “$” 开头的数字
MatchCollection matches = Regex.Matches(input, pattern);
if (matches.Count > 0)
{
Console.WriteLine("价格:");
foreach (Match match in matches)
{
Console.WriteLine(" - " + match.Value);
}
}
else
{
Console.WriteLine("未找到匹配项!");
}
}
}
“`
3. 使用 Regex.Replace
进行高级替换
Regex.Replace
方法不仅可以替换匹配的字符串,还可以使用 MatchEvaluator
委托进行更复杂的替换逻辑。
“`csharp
using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main(string[] args)
{
string input = “The temperature is 25C.”;
string pattern = @”(\d+)C”;
string result = Regex.Replace(input, pattern, m =>
{
int celsius = int.Parse(m.Groups[1].Value);
double fahrenheit = (celsius * 9.0 / 5.0) + 32;
return fahrenheit.ToString("F2") + "F";
});
Console.WriteLine("转换后的字符串: " + result); // Output: The temperature is 77.00F.
}
}
“`
五、性能考虑
正则表达式的性能取决于多种因素,包括正则表达式的复杂性、输入字符串的大小以及使用的 RegexOptions
。
- 避免过度复杂的正则表达式: 尽量简化正则表达式,减少回溯。
- 使用
Compiled
选项: 对于需要多次使用的正则表达式,使用Compiled
选项可以显著提高性能。 - 避免在循环中创建
Regex
对象: 在循环外部创建Regex
对象,并重用它。 - 考虑使用字符串操作代替简单的正则表达式: 对于简单的字符串查找和替换,使用
string.Contains
、string.Replace
等方法可能更有效率。
六、总结
C# 中的正则表达式提供了强大的文本处理能力。通过掌握 Regex
类的常用方法、正则表达式语法以及 RegexOptions
,可以轻松地提取匹配项并按行显示。本文详细介绍了正则表达式的基础知识、提取匹配项的各种方法,并提供了丰富的示例代码和高级技巧,帮助读者在实际开发中灵活运用正则表达式,提高开发效率。记住,编写高效的正则表达式需要经验和不断的实践。希望本文能够帮助你更好地理解和使用 C# 中的正则表达式,从而解决各种文本处理问题。 不断尝试,不断学习,你将能够熟练掌握正则表达式的强大功能。