所以我有一个网站,我需要在一个巨大的文本文件(~2GB)中访问一行(行号已知)。
我得出结论
PHP中的system_exec(“sed -n 3p<< /file/whatever.txt”);
是最有效的方式。
但是我觉得使用它感觉不太舒服,这似乎是一个糟糕的黑客和不安全感。使用它真的没问题吗?如果没有PHP框架,这种方式是否可行?或者有更有效的方法来做到这一点吗?
答案 0 :(得分:2)
在巨型文件中打印单行的最快方法是使用q(退出)命令
sed -n '3{p;q}' yourFile
这将打印第3行,然后sed将停止工作。
答案 1 :(得分:1)
以下是您可以将各种方法转移到文件中的方法,以及一些粗略的基准测试。
我创建了一个包含90M行的文本文件。每行包含一些#####'尽管这些数字与实际行不匹配(以便更快地创建样本数据)。
$ wc bigfile.txt
90000000 90000000 1340001000 bigfile.txt
$ ls -lrth bigfile.txt
-rw-rw-r-- 1 admin wheel 1.2G Mar 8 09:37 bigfile.txt
这些基准测试是在运行OS 10.10.2的1.3GHz i5,4GB RAM,MacBook Air(11英寸,2013年中)上进行的。
首先,是awk
。我真的期待更好。
$ time awk 'NR == 10000000{print;exit}' bigfile.txt
something99999
real 0m12.716s
user 0m12.529s
sys 0m0.117s
tail
表现稍好,但仍然很慢。
$ time tail -n +10000000 bigfile.txt | head -n 1
something99999
real 0m10.393s
user 0m10.311s
sys 0m0.066s
正如您所知,由于某种原因,sed
方式迄今为止优于其他竞争者。但是,仍然慢得令人无法接受。
$ time sed -n '10000000{p;q;}' bigfile.txt
something99999
real 0m3.846s
user 0m3.772s
sys 0m0.053s
如果您有常规数据(每行相同的字节数或可以确定地计算每行的字节数),您可以放弃完全读取文件并直接偏移到文件中。这是最快的选择,但在数据格式方面也是最严格的选择。这就是William Pursell在建议将数据填充到固定大小时所得到的。
$ time tail -c +10000000 bigfile.txt | head -n 1
thing71851
real 0m0.020s
user 0m0.011s
sys 0m0.006s
但是,如果您有2G文本文件,则应考虑使用正确的数据库。
$ time sqlite3 bigfile.db << EOF
> create table bigdb(data text);
> .import bigfile.txt bigdb
> EOF
real 3m16.650s
user 3m3.703s
sys 0m4.221s
$ ls -lrth bigfile.db
-rw-r--r-- 1 admin wheel 1.9G Mar 8 10:16 bigfile.db
既然你有一个数据库,你应该能够获得超快的速度吗?只有你正确使用它。 OFFSET
(LIMIT
的第一个参数)因为速度太慢而臭名昭着,应该避免。
$ time sqlite3 bigfile.db <<< 'select * from bigdb limit 10000000-1, 1;'
something99999
real 0m2.156s
user 0m0.688s
sys 0m0.440s
您应该拥有正确的主键,或使用sqlite方便的内部列ROWID
来获得最佳效果。
$ time sqlite3 bigfile.db <<< 'select * from bigdb where ROWID == 10000000;'
something99999
real 0m0.017s
user 0m0.003s
sys 0m0.005s
答案 2 :(得分:0)
在我的系统上,我得出了完全不同的结论 Environnement:KSH下的AIX
FileName=listOfBig.txt
# ls -l -> 239.070.208 bytes
# wc -l listOfBig.txt | read FileSize Ignore
FileSize=638976
# take a portion of 8 lines at 1000 lines of the end
LineToStart=$(( ${FileSize} - 1024 ))
LineToTake=8
LineToStop=$(( ${LineToStart} + ${LineToTake} - 1 ))
time sed -n "${LineToStart},${LineToStop} p;${LineToStop} q" ${FileName} >/dev/null
real 0m1.49s
user 0m0.45s
sys 0m0.41s
time sed "${LineToStart},${LineToStop} !d;${LineToStop} q" ${FileName} >/dev/null
real 0m1.51s
user 0m0.45s
sys 0m0.42s
time tail -n +${LineToStart} ${FileName} | head -${LineToTake} >/dev/null
real 0m0.34s
user 0m0.00s
sys 0m0.00s
time head -${LineToStop} ${FileName} | tail -${LineToTake} >/dev/null
real 0m0.84s
user 0m0.75s
sys 0m0.23s
第二次和后续测试肯定有一个小优势(第一次(缓存,...)但不是很不一样
所以,在这个测试中,sed要慢得多(不是像Linux上那样的GNU版工具)。
如果文件正在发生变化(通常是日志中的情况),还有另一个问题是在巨大文件的情况下无法解释(可能发生在很小但很少发生)是管道流的问题。我曾经遇到过这个问题,应该创建一个临时文件(也非常大),以便处理该行的其他请求(如果有的话)。