从文件中读取随机行的简单方法是啥?
Posted
技术标签:
【中文标题】从文件中读取随机行的简单方法是啥?【英文标题】:What's an easy way to read random line from a file?从文件中读取随机行的简单方法是什么? 【发布时间】:2010-10-01 16:07:13 【问题描述】:在 shell 脚本中从文件中读取随机行的简单方法是什么?
【问题讨论】:
每行是否填充到固定长度? 不,每行有可变数量的字符 大文件:***.com/questions/29102589/… 【参考方案1】:你可以使用shuf
:
shuf -n 1 $FILE
还有一个名为rl
的实用程序。在 Debian 中,它位于 randomize-lines
软件包中,可以完全满足您的需求,但并非在所有发行版中都可用。在它的主页上,它实际上建议使用shuf
(我相信它在创建时并不存在)。 shuf
是 GNU coreutils 的一部分,rl
不是。
rl -c 1 $FILE
【讨论】:
感谢shuf
的提示,它是 Fedora 内置的。
另外,sort -R
如果处理相当大的文件(80kk 行),肯定会让人们等待很多,而shuf -n
会立即行动.
您可以通过从 Homebrew 安装 coreutils
在 OS X 上获得 shuf。可能被称为 gshuf
而不是 shuf
。
同样,你可以在 OS X 上使用randomize-lines
brew install randomize-lines; rl -c 1 $FILE
请注意,shuf
是GNU Coreutils 的一部分,因此在 *BSD 系统(或 Mac?)上不一定可用(默认情况下)。 @Tracker1 下面的 perl 单行代码更便携(根据我的测试,速度稍快)。【参考方案2】:
另一种选择:
head -$(($RANDOM % `wc -l < file` + 1)) file | tail -1
【讨论】:
$RANDOM 只生成小于 32768 的数字,因此不要将其用于大文件(例如英语词典)。 由于模运算,这不会为您提供每行的精确相同概率。如果文件长度为 您可以使用($RANDOM << 15) + $RANDOM
将其扩展到 30 位随机数。这显着减少了偏差,并使其适用于包含多达 10 亿行的文件。
@nneonneo:非常酷的技巧,虽然根据这个链接,它应该是 OR'ing $RANDOM's 而不是 PLUS'ing ***.com/a/19602060/293064
+
和 |
是相同的,因为 $RANDOM
的定义是 0..32767。【参考方案3】:
sort --random-sort $FILE | head -n 1
(不过,我更喜欢上面的 shuf 方法——我什至不知道它的存在,而且我自己也永远找不到那个工具)
【讨论】:
+1 我喜欢它,但您可能需要一个最近的sort
,在我的任何系统(CentOS 5.5、Mac OS 10.7.2)上都不起作用。另外,无用的猫,可以减少到sort --random-sort < $FILE | head -n 1
sort -R <<< $'1\n1\n2' | head -1
返回 1 和 2 的可能性相同,因为 sort -R
将重复的行排序在一起。这同样适用于sort -Ru
,因为它会删除重复的行。
这相对较慢,因为整个文件需要先由sort
洗牌,然后再通过管道传送到head
。 shuf
从文件中选择随机行,而不是对我来说更快。
@SteveKehlet 最好是sort --random-sort $FILE | head
,因为它允许它直接访问文件,可能实现高效的并行排序
--random-sort
和 -R
选项特定于 GNU 排序(因此它们不适用于 BSD 或 Mac OS sort
)。 GNU sort 在 2005 年学习了这些标志,因此您需要 GNU coreutils 6.0 或更高版本(例如 CentOS 6)。【参考方案4】:
这很简单。
cat file.txt | shuf -n 1
当然,这比“shuf -n 1 file.txt”本身慢一点。
【讨论】:
最佳答案。我不知道这个命令。注意-n 1
指定1行,你可以改成多于1行。shuf
也可以用于其他用途;我只是通过管道传输 ps aux
和 grep
以随机杀死部分匹配名称的进程。【参考方案5】:
perlfaq5: How do I select a random line from a file? 这是来自骆驼书的水库采样算法:
perl -e 'srand; rand($.) < 1 && ($line = $_) while <>; print $line;' file
与读取整个文件相比,这在空间上具有显着优势。您可以在 Donald E. Knuth 的 The Art of Computer Programming, Volume 2, Section 3.4.2 中找到这种方法的证明。
【讨论】:
只是为了包含的目的(以防被引用的站点出现故障),这是 Tracker1 指向的代码:“cat filename | perl -e 'while () push(@_ ,$_); 打印@_[rand()*@_];';" 这是一个无用的cat。这是对 perlfaq5 中代码的轻微修改(并由 Camel 书提供): perl -e 'srand; rand($.) ;打印 $line;'文件名 err...链接的网站,也就是 我刚刚将此代码的 N 行版本与shuf
进行了基准测试。 perl 代码稍微快一点(用户时间快 8%,系统时间快 24%),但有趣的是,我发现 perl 代码“似乎”不那么随机(我用它写了一个点唱机)。
更多思考:shuf
stores the whole input file in memory,这是一个可怕的想法,而这段代码只存储一行,所以这段代码的限制是行数为 INT_MAX (2^31 或 2^ 63 取决于您的拱门),假设任何选定的潜在线路都适合内存。【参考方案6】:
使用 bash 脚本:
#!/bin/bash
# replace with file to read
FILE=tmp.txt
# count number of lines
NUM=$(wc - l < $FILE)
# generate random number in range 0-NUM
let X=$RANDOM % $NUM + 1
# extract X-th line
sed -n $Xp $FILE
【讨论】:
随机可以为0,sed第一行需要1。 sed -n 0p 返回错误。 mhm - "tmp.txt" 1 美元和 NUM 2 美元怎么样? 但即使有一个值得一提的错误,因为它不需要 perl 或 python 并且尽可能高效(准确地读取文件两次但不进入内存 - 所以它甚至可以使用大文件)。 @asalamon74:谢谢@blabla999:如果我们用它做一个函数,1 美元就可以了,但为什么不计算 NUM? 将 sed 行更改为:head -$X $FILE | tail -1 应该这样做【参考方案7】:单个 bash 行:
sed -n $((1+$RANDOM%`wc -l test.txt | cut -f 1 -d ' '`))p test.txt
小问题:文件名重复。
【讨论】:
小问题。在 /usr/share/dict/words 上执行此操作倾向于支持以“A”开头的单词。玩它,我大约有 90% 的“A”字到 10% 的“B”字。还没有以数字开头的,它们构成了文件的头部。wc -l < test.txt
避免了必须通过管道传输到 cut
。【参考方案8】:
这里有一个简单的 Python 脚本可以完成这项工作:
import random, sys
lines = open(sys.argv[1]).readlines()
print(lines[random.randrange(len(lines))])
用法:
python randline.py file_to_get_random_line_from
【讨论】:
这不太行。它在一行之后停止。为了使它工作,我这样做了:import random, sys lines = open(sys.argv[1]).readlines()
for i in range(len(lines)): rand = random.randint(0, len(lines)-1) printlines.pop(rand),
带有蹩脚格式的愚蠢评论系统。 cmets 中的格式化从前不是有效的吗?
randint 是包容性的,因此 len(lines)
可能会导致 IndexError。你可以使用print(random.choice(list(open(sys.argv[1]))))
。还有内存高效reservoir sampling algorithm。
相当空间;考虑一个 3TB 的文件。
@MichaelCampbell: reservoir sampling algorithm 我上面提到的可能适用于 3TB 文件(如果行大小有限)。【参考方案9】:
使用'awk'的另一种方式
awk NR==$(($RANDOM % `wc -l < file.name` + 1)) file.name
【讨论】:
使用 awk 和 bash($RANDOM
是 bashism)。这是一个纯 awk (mawk) 方法,使用与上面 @Tracker1 引用的 perlfaq5 代码相同的逻辑:awk 'rand() * NR < 1 line = $0 END print line ' file.name
(哇,它甚至比 perl 代码短!)
该代码必须读取文件 (wc
) 以获得行数,然后必须再次读取(部分)文件 (awk
) 以获得给定随机数的内容电话号码。 I/O 将比获取随机数昂贵得多。我的代码只读取一次文件。 awk 的 rand()
的问题在于它基于秒数播种,因此如果连续运行太快,您会得到重复。【参考方案10】:
同样适用于 MacOSX 的解决方案,也应该适用于 Linux(?):
N=5
awk 'NR==FNR lineN[$1]; next(FNR in lineN)' <(jot -r $N 1 $(wc -l < $file)) $file
地点:
N
是你想要的随机行数
NR==FNR lineN[$1]; next(FNR in lineN) file1 file2
--> 保存file1
中写的行号,然后打印file2
中对应的行
jot -r $N 1 $(wc -l < $file)
--> 在(1, number_of_line_in_file)
和jot
范围内随机抽取N
数字(-r
)。进程替换 <()
将使它看起来像解释器的文件,因此在前面的示例中为 file1
。
【讨论】:
【参考方案11】:#!/bin/bash
IFS=$'\n' wordsArray=($(<$1))
numWords=$#wordsArray[@]
sizeOfNumWords=$#numWords
while [ True ]
do
for ((i=0; i<$sizeOfNumWords; i++))
do
let ranNumArray[$i]=$(( ( $RANDOM % 10 ) + 1 ))-1
ranNumStr="$ranNumStr$ranNumArray[$i]"
done
if [ $ranNumStr -le $numWords ]
then
break
fi
ranNumStr=""
done
noLeadZeroStr=$((10#$ranNumStr))
echo $wordsArray[$noLeadZeroStr]
【讨论】:
由于 $RANDOM 生成的数字少于 /usr/share/dict/words 中的字数,它有 235886(无论如何在我的 Mac 上),我只生成 0 到 9 之间的 6 个单独的随机数并将它们串在一起。然后我确保该数字小于 235886。然后删除前导零以索引我存储在数组中的单词。由于每个单词都有自己的一行,因此可以很容易地用于任何文件来随机选择一行。【参考方案12】:这是我发现的,因为我的 Mac OS 没有使用所有简单的答案。我使用 jot 命令生成一个数字,因为在我的测试中 $RANDOM 变量解决方案似乎不是很随机。在测试我的解决方案时,输出中提供的解决方案存在很大差异。
RANDOM1=`jot -r 1 1 235886`
#range of jot ( 1 235886 ) found from earlier wc -w /usr/share/dict/web2
echo $RANDOM1
head -n $RANDOM1 /usr/share/dict/web2 | tail -n 1
变量的回显是为了得到生成的随机数的视觉效果。
【讨论】:
【参考方案13】:仅使用 vanilla sed 和 awk,而不使用 $RANDOM,一个简单、节省空间且相当快速的“单行”,用于从名为 FILENAME 的文件中伪随机选择单行,如下所示:
sed -n $(awk 'END srand(); r=rand()*NR; if (r<NR) sub(/\..*/,"",r); r++;; print r' FILENAME)p FILENAME
(即使 FILENAME 为空也有效,在这种情况下不会发出任何行。)
这种方法的一个可能优点是它只调用 rand() 一次。
正如@AdamKatz 在 cmets 中指出的那样,另一种可能性是为每一行调用 rand():
awk 'rand() * NR < 1 line = $0 END print line ' FILENAME
(可以基于归纳给出简单的正确性证明。)
注意rand()
“在大多数 awk 实现中,包括 gawk,rand() 每次运行 awk 时都会从相同的起始数字或种子开始生成数字。”
-- https://www.gnu.org/software/gawk/manual/html_node/Numeric-Functions.html
【讨论】:
请参阅the comment I posted a year before this answer,它有一个更简单的不需要 sed 的 awk 解决方案。还要注意我对 awk 的随机数生成器的警告,它会在整秒内播种。以上是关于从文件中读取随机行的简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章