C#中Regex启用Multiline仍无法识别多行及重复匹配异常
C#正则表达式捕获扑克手牌文本的问题解决
问题场景
在C#中使用Regex解析扑克手牌文本时遇到以下问题,相关代码与目标文本如下:
正则表达式代码
string pattern = @"([\w\d]+) Hand #([\d]+): Tournament #([\d]+), ([$€])([.\d]+)\+[$€]([.\d]+).+\s\(([\d]+\/[\d]+)\).+([\d]+\/[\d]+\/[\d]+ )([\d:]+) ET" + @"(\n|\r|\r\n)Table '([\s\d]+)' (.+) Seat #(\d) is the button" + @"((?:(?:\n|\r|\r\n)^Seat ([\d]+): (.+) \(([\d]+) in chips\))*)"; MatchCollection matches = Regex.Matches(_hh, pattern, RegexOptions.Multiline);
目标文本示例
PokerStars Hand #232702710836: Tournament #3332581238, $9.22+$0.78 USD Hold'em No Limit - Level IV (40/80) - 2021/12/31 22:34:19 ET Table '3332581238 1' 9-max Seat #3 is the button Seat 1: mpolishuk (2018 in chips) Seat 3: Kevin2049 (1154 in chips) Seat 4: IPray2Buddha (1030 in chips) Seat 5: Sakura2892 (1499 in chips) Seat 7: Lillien66 (2141 in chips) Seat 9: owlie45 (5658 in chips) mpolishuk: posts the ante 10 Kevin2049: posts the ante 10 IPray2Buddha: posts the ante 10 Sakura2892: posts the ante 10 Lillien66: posts the ante 10 owlie45: posts the ante 10 IPray2Buddha: posts small blind 40 Sakura2892: posts big blind 80 *** HOLE CARDS *** Dealt to IPray2Buddha [6c 6h] Lillien66: folds owlie45: folds mpolishuk: folds Kevin2049: folds IPray2Buddha: calls 40 Sakura2892: raises 1409 to 1489 and is all-in IPray2Buddha: calls 940 and is all-in Uncalled bet (469) returned to Sakura2892 *** FLOP *** [8d 7c Ts] *** TURN *** [8d 7c Ts] [6s] *** RIVER *** [8d 7c Ts 6s] [Ah] *** SHOW DOWN *** IPray2Buddha: shows [6c 6h] (three of a kind, Sixes) Sakura2892: shows [Kd Qh] (high card Ace) IPray2Buddha collected 2100 from pot *** SUMMARY *** Total pot 2100 | Rake 0 Board [8d 7c Ts 6s Ah] Seat 1: mpolishuk folded before Flop (didn't bet) Seat 3: Kevin2049 (button) folded before Flop (didn't bet) Seat 4: IPray2Buddha (small blind) showed [6c 6h] and won (2100) with three of a kind, Sixes Seat 5: Sakura2892 (big blind) showed [Kd Qh] and lost with high card Ace Seat 7: Lillien66 folded before Flop (didn't bet) Seat 9: owlie45 folded before Flop (didn't bet)
遇到的核心问题
- 已启用
RegexOptions.Multiline,但正则无法识别\n换行符与^行首锚点 - 重复匹配Seat行的表达式仅能捕获第一个条目,使用
*或重复书写表达式均无效
问题分析与解决办法
1. 换行符与锚点失效问题
原因
- C#逐字字符串(
@"")中,\n会被解析为字面量字符,而非转义换行符;原表达式中(\n|\r|\r\n)写法冗余且未正确适配逐字字符串的转义规则 RegexOptions.Multiline下^匹配行首,但如果换行符匹配错误,^无法定位到Seat行的开头位置
解决
- 用
\r?\n代替(\n|\r|\r\n),简洁兼容Windows(\r\n)与Unix(\n)换行格式 - 确保
^锚点前的换行匹配正确,逐字字符串中\r?\n无需额外转义,可直接使用
2. 重复匹配仅捕获第一个Seat条目问题
原因
正则表达式中,重复的捕获组(如(...)*)只会保留最后一次匹配的结果,无法存储所有重复条目,这是正则的原生限制
解决(推荐拆分匹配,更易维护)
将匹配逻辑拆分为两部分:先捕获手牌头部信息,再单独匹配所有Seat行
// 匹配手牌头部信息(Hand、Tournament、Table、Button) string headerPattern = @"^([\w\d]+) Hand #(\d+): Tournament #(\d+), ([€$])([\d.]+)\+[€$]([\d.]+).+\s\((\d+\/\d+)\).+(\d+\/\d+\/\d+ )(\d+:+) ET\r?\nTable '([\s\d]+)' (.+) Seat #(\d) is the button"; Match headerMatch = Regex.Match(_hh, headerPattern, RegexOptions.Multiline); // 匹配所有Seat行 string seatPattern = @"^Seat (\d+): (.+) \((\d+) in chips\)$"; MatchCollection seatMatches = Regex.Matches(_hh, seatPattern, RegexOptions.Multiline); // 处理头部信息 if (headerMatch.Success) { string site = headerMatch.Groups[1].Value; long handId = long.Parse(headerMatch.Groups[2].Value); long tournamentId = long.Parse(headerMatch.Groups[3].Value); string currencySymbol = headerMatch.Groups[4].Value; decimal buyin = decimal.Parse(headerMatch.Groups[5].Value); decimal rake = decimal.Parse(headerMatch.Groups[6].Value); string levelBlinds = headerMatch.Groups[7].Value; string date = headerMatch.Groups[8].Value.Trim(); string time = headerMatch.Groups[9].Value; string tableName = headerMatch.Groups[10].Value; string tableType = headerMatch.Groups[11].Value; int buttonSeat = int.Parse(headerMatch.Groups[12].Value); // 按需存储头部数据 } // 处理每个Seat的玩家信息 foreach (Match seatMatch in seatMatches) { int seatNum = int.Parse(seatMatch.Groups[1].Value); string playerName = seatMatch.Groups[2].Value; int chipCount = int.Parse(seatMatch.Groups[3].Value); // 按需存储玩家数据 }
备选方案(单个正则捕获后拆分)
若需用单个正则,可先捕获所有Seat行的文本块,再拆分解析:
string fullPattern = @"^([\w\d]+) Hand #(\d+): Tournament #(\d+), ([€$])([\d.]+)\+[€$]([\d.]+).+\s\((\d+\/\d+)\).+(\d+\/\d+\/\d+ )(\d+:+) ET\r?\nTable '([\s\d]+)' (.+) Seat #(\d) is the button\r?\n((?:Seat \d+: .+ \(\d+ in chips\)\r?\n)*)"; Match fullMatch = Regex.Match(_hh, fullPattern, RegexOptions.Multiline); if (fullMatch.Success) { // 处理头部信息... string seatsTextBlock = fullMatch.Groups[12].Value; // 拆分Seat行并逐个解析 string[] seatLines = seatsTextBlock.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); foreach (string line in seatLines) { Match seatMatch = Regex.Match(line, @"Seat (\d+): (.+) \((\d+) in chips\)"); if (seatMatch.Success) { // 处理单个Seat数据 } } }
内容的提问来源于stack exchange,提问作者HungryCroco
相关产品推荐
相关产品推荐

