AWK处理多行字符串无重复随机抽取10字符的问题及解决
从指定列字符串中随机无重复抽取10个字符的实现与问题解决
需求说明
从文件指定列的字符串中,随机无重复抽取10个字符(此处的无重复指不重复抽取同一索引位置的字符):
- 示例1:对字符串
ABCDEFGHIJKLMN抽取,结果无重复字符 - 示例2:对字符串
CCCCCCCCCCCCGG抽取,结果中G最多出现2次(原字符串仅含2个G的索引位置)
Bash实现及局限
用Bash命令可实现需求,但处理数十万行的多文件时效率极低:
echo "ABCDEFGHIJKLMN" | fold -w1 | shuf -n10 | tr -d '\n'
AWK实现的问题与修复
初始单行AWK代码(正常运行)
单行处理时可正常工作:
awk '{srand(); len=length($1); for(i=1;i<=10;) {k=int(rand()*len)+1; if(!(k in N)) {N[k]; printf "%s", substr($1,k,1); i++}} print ""}' <(echo "ABCDEFGHIJKLMN")
多行文件的问题代码(卡顿、输出截断)
处理多行文件时出现卡顿且输出截断:
echo "ABCDEFGHIJKLMN" > file.txt echo "CCCCCCCCCCCCGG" >> file.txt awk '{srand(); len=length($1); for(i=1;i<=10;) {k=int(rand()*len)+1; if(!(k in N)) {N[k]; printf "%s", substr($1,k,1); i++}} print ""}' file.txt
问题原因:AWK中的数组N是全局变量,处理完一行后未重置,下一行处理时会继承之前的索引记录,导致可选择的有效索引越来越少,甚至陷入循环无法凑够10个字符。
可重复抽取的AWK代码(无问题)
对比可重复抽取的代码可正常运行,进一步验证问题出在N数组的全局保留:
awk '{srand(); len=length($1); for(i=1;i<=10;i++) {k=int(rand()*len)+1; printf "%s", substr($1,k,1)} print ""}'
修复后的AWK代码
通过split("", N)重置数组解决问题,同时将srand()放在BEGIN块避免每行重置随机种子:
awk 'BEGIN{srand()} {len=length($1); for(i=1;i<=10;) {k=int(rand()*len)+1; if(!(k in N)) {N[k]; printf "%s", substr($1,k,1); i++}} split("", N); print ""}' file.txt
注意事项与替代方案
- 若字符串长度小于10,上述修复后的代码仍会陷入无限循环(无法抽取10个不重复的索引位置)
- 替代方案:抽取数量取字符串长度和10的较小值,避免循环无法终止,示例逻辑修改如下:
awk 'BEGIN{srand()} {len=length($1); take=(len<10?len:10); for(i=1;i<=take;) {k=int(rand()*len)+1; if(!(k in N)) {N[k]; printf "%s", substr($1,k,1); i++}} split("", N); print ""}' file.txt
内容的提问来源于stack exchange,提问作者Arynio
相关产品推荐
相关产品推荐

