解析PokerStars手牌历史的awk脚本循环变量覆盖、输出错位问题
1. 记录分隔符配置与实际文本格式不匹配
脚本中设置的RS="\n\r\n\r\n\r\n"搞反了换行符和回车符的顺序。Windows平台导出的PokerStars手牌,每行结尾是标准的\r\n(先回车回到行首,再换行到下一行),两个手牌之间的三个空行对应的实际分隔串是\r\n\r\n\r\n。因为分隔符配置错误,awk无法正确拆分独立的手牌记录,第一份手牌的开头标识行根本没被归入对应记录的处理范围,直接导致第一条记录的元数据(手牌ID、游戏类型、时间)全部为空。
2. 残留回车符导致输出错位
脚本用FS="\n"拆分每条记录的行时,拆出来的每一行末尾都带着Windows格式的\r(回车符)。这个字符在终端输出时会强制把光标移回当前行的最开头,后续打印的内容会直接覆盖行首的已有文本——你看到的「字段拼接重叠、内容串到上一行」的错位输出,本质就是这个回车符造成的,和用print还是printf没有关系。
3. 全局变量无重置造成值串扰
awk的变量默认是全局生命周期,如果不主动在每条记录处理前把临时提取的变量(handID/gameType/dateTime/holeCards)重置为空值,一旦某条记录没匹配到对应的标识行,上一条记录残留的旧值就会被带出来,进一步加剧字段错位。
4. split字段索引计算错位
用空格作为split分隔符时,awk会自动把连续的多个空白符合并为一个分隔边界,脚本中写的数组索引(比如aHand[3]、aHand[5]等)完全没对应上实际拆分后的数组位置,既漏了游戏类型里的Hold'em部分,也经常提取不到正确的字段值。
- 将RS改成兼容多平台换行的正则形式,不管是
\r\n还是\n格式的分隔都能正确识别 - 每条记录处理的最开头,先重置所有临时提取变量,避免跨记录的值串扰
- 逐行清理行尾的
\r字符,从根源解决输出错位问题 - 用正则匹配的方式直接从行文本里提取目标字段,不再逐个数split的空格位置,避免文本格式微调就失效
BEGIN{ # 兼容Windows/Unix格式的三空行记录分隔 RS="\r?\n\r?\n\r?\n" FS="\n" OFS="," print "Hand ID,Game Type,Time,Holecards" } { # 每条记录处理前先重置所有临时变量,避免值串扰 handID = gameType = dateTime = holeCards = "" for (i = 1; i <= NF; i++){ # 清理每一行末尾的多余回车符 sub(/\r$/, "", $i) # 匹配手牌开头行,提取手牌ID、游戏类型、时间 if (match($i, /^PokerStars Hand #([0-9]+):\s+(.+) - ([0-9\/]+ [0-9:]+ ET)$/, matchRes)){ handID = "#" matchRes[1] ":" gameType = matchRes[2] dateTime = matchRes[3] } # 匹配自己的底牌行 if (match($i, /^Dealt to me \[(.+)\]$/, matchCard)){ holeCards = "[" matchCard[1] "]" } } print(handID, gameType, dateTime, holeCards) }
注:上述脚本基于Gawk 5.1及以上版本编写,如果使用的旧版gawk不支持match函数的第三参数,可以手动用substr截取对应位置的内容,核心逻辑保持一致即可。
内容的提问来源于stack exchange,提问作者DavidHk

