从文件中读取随机行的简单方法是啥?

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 &lt;&lt; 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 &lt; $FILE | head -n 1 sort -R &lt;&lt;&lt; $'1\n1\n2' | head -1 返回 1 和 2 的可能性相同,因为 sort -R 将重复的行排序在一起。这同样适用于sort -Ru,因为它会删除重复的行。 这相对较慢,因为整个文件需要先由sort 洗牌,然后再通过管道传送到headshuf 从文件中选择随机行,而不是对我来说更快。 @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 auxgrep 以随机杀死部分匹配名称的进程。【参考方案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 &lt; 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 &lt; 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 &lt; $file) --> 在(1, number_of_line_in_file)jot 范围内随机抽取N 数字(-r)。进程替换 &lt;() 将使它看起来像解释器的文件,因此在前面的示例中为 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 的随机数生成器的警告,它会在整秒内播种。

以上是关于从文件中读取随机行的简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

从巨大的 CSV 文件中读取随机行

读取随机行 com 文件

R:使用 fread 或等价物从文件中读取随机行?

Clojure 懒惰地从文件中读取随机行

从STDIN读取文件并打印随机行。

读取大型 csv 文件、python、pandas 的随机行