在C#里提取一段文本中的中文字符,最直接高效的办法就是使用正则表达式配合Unicode字符区间进行匹配。.NET的Regex类对Unicode的支持非常完善,只要正则写对,无论是简体、繁体还是中英文混杂的字符串都能准确处理。下面从基本写法、常用方法选择、边界情况处理三个角度把这件事讲透。

一、最基础的中文匹配正则写法
中文字符在Unicode编码表中主要分布在4E00到9FA5这个区间,也就是常说的CJK统一表意文字基本区。所以提取中文的正则通常写成[u4e00-u9fa5],在C#字符串里需要配合转义符使用。最简单的提取代码如下:
using System;
using System.Text.RegularExpressions;
using System.Linq;
class Program
{
static void Main()
{
string input = "Hello世界,C#开发者2024!Hello,World!";
// 匹配单个连续的中文串
MatchCollection matches = Regex.Matches(input, "[\u4e00-\u9fa5]+");
foreach (Match m in matches)
{
Console.WriteLine(m.Value);
}
// 输出:世界 开发者
}
}注意正则末尾的加号很关键,它表示匹配一个或多个连续汉字,这样“世界”会被作为一个整体提取出来,而不是拆成“世”和“界”两个单独结果。如果你确实需要逐字提取,把加号去掉即可。另外这里的u4e00写成转义形式,也可以直接在正则字符串里写字面汉字,例如匹配规则直接写成范围形式也是合法的,两种写法效果完全一样。
还有一种写法是利用.NET正则的Unicode类别,使用p{IsCJKUnifiedIdeographs}来匹配,语义上更明确:
string input = "混合文字text示例demo";
var result = Regex.Matches(input, @"\p{IsCJKUnifiedIdeographs}+");
foreach (Match m in result)
{
Console.WriteLine(m.Value); // 输出:混合文字 示例
}这种Unicode类别写法的好处是可读性强,别人一眼能看出你在匹配CJK统一表意文字,缺点是写起来比较长。日常项目里用区间写法更普遍,团队代码规范允许的话,两种都可以。
二、IsMatch、Matches、Replace怎么选
Regex类提供了多个静态方法,很多初学者在提取文字时容易用错。这里明确一下各自的适用场景:IsMatch只判断是否存在匹配,返回布尔值,适合校验场景,比如验证用户输入必须包含中文;Matches用于提取所有匹配结果,是提取中文的主力方法;Replace则是把匹配内容替换掉,反过来用它把非中文字符删掉,也能达到提取效果。
来看一个反向删除的例子,利用非中文字符的否定匹配,把所有非汉字内容替换成空字符串:
string input = "订单号:A12345,金额:99.5元,状态:已支付"; // 把非中文字符全部替换为空,剩下纯中文 string onlyChinese = Regex.Replace(input, @"[^\u4e00-\u9fa5]", ""); Console.WriteLine(onlyChinese); // 输出:订单号金额元状态已支付
这种方式的局限也很明显:所有汉字会被拼接成一整串,原本的词语边界丢失了。如果需要保留词语分组,用Matches才是正确选择。另外还有一个实用技巧是取反匹配判断字符串是否纯中文:
static bool IsAllChinese(string text)
{
// 存在非中文字符就返回false
return !Regex.IsMatch(text, @"[^\u4e00-\u9fa5]") && text.Length > 0;
}关于性能,如果同一条正则要在循环里反复使用,建议把Regex对象用Compiled选项预编译,或者.NET 7以后直接用[GeneratedRegex]特性生成源代码级别的正则,能显著减少重复解析开销:
public partial class ChineseExtractor
{
[GeneratedRegex(@"[\u4e00-\u9fa5]+")]
private static partial Regex ChineseRegex();
public static List<string> Extract(string input)
{
return ChineseRegex().Matches(input)
.Select(m => m.Value)
.ToList();
}
}三、扩展汉字区、中文标点与常见坑
4E00到9FA5这个区间覆盖了常用的两万多个汉字,但并不完整。像“㙟”“𠮷”这类生僻字位于扩展A区和扩展B区,基本区间的正则匹配不到它们。如果业务涉及人名、古籍等可能包含生僻字的文本,建议扩大匹配范围或直接使用Unicode类别。扩展B区的范围是20000到2A6DF,可以把正则写成多个区间的并集:
string pattern = @"[\u4e00-\u9fa5\u3400-\u4dbf]|[\uD840-\uD87F][\uDC00-\uDFFF]";
// 第二部分是扩展B区的代理对写法,.NET字符串以UTF-16存储,超出基本平面的字符由两个char组成
var matches = Regex.Matches("𠮷天大利,常见汉字测试", pattern);
foreach (Match m in matches)
{
Console.WriteLine(m.Value);
}第二个常见的坑是中文标点符号。逗号、句号、顿号这些全角标点不在4E00到9FA5区间内,所以默认的中文正则不会匹配它们。如果你的“提取中文”需求包含中文标点,需要额外加上标点区间,例如3000到303F是CJK符号和标点区,FF00到FFEF是全角字符区:
string input = "你好,世界!这是一个测试。";
// 同时匹配汉字和中文标点
var result = Regex.Matches(input, @"[\u4e00-\u9fa5\u3000-\u303F\uFF00-\uFFEF]+");
foreach (Match m in result)
{
Console.WriteLine(m.Value); // 输出:你好,世界!这是一个测试。
}第三点要注意的是字符串编码问题。C#的string内部采用UTF-16编码,从文件或网络读取文本时,如果源编码不是UTF-8或UTF-16,要先用正确的Encoding解码成string再做正则匹配,否则乱码字符根本无法被中文区间命中。典型的错误是读取GBK编码的文件时用了默认编码,结果所有汉字都变成问号,正则自然什么也匹配不到。
// 读取GBK编码文件时必须显式指定编码
using System.Text;
string content = File.ReadAllText(@"D:\data\input.txt", Encoding.GetEncoding("GBK"));
var chineseParts = Regex.Matches(content, @"[\u4e00-\u9fa5]+")
.Select(m => m.Value);
Console.WriteLine(string.Join(" ", chineseParts));总结一下:日常提取中文用[u4e00-u9fa5]+配合Regex.Matches就够了;需要兼容生僻字就加上扩展区或用Unicode类别写法;涉及中文标点要补充符号区间;处理外部数据源时先确保编码正确。把这几个要点记住,C#提取中文字符基本不会再遇到问题。