提问人:sdfsdf 提问时间:10/8/2023 最后编辑:sdfsdf 更新时间:10/28/2023 访问量:130
如何仅用一行搜索 104GB 文件中的特定文本?
How to search specific text in 104GB file with only one line?
问:
我刚刚使用 y-cruncher 计算完 PI 数的 1110 亿位数字,还有 104GB 的文件。
我遇到了一个问题。
我想玩一玩,试着在那里搜索名称、值、标题等,那是某个数字序列。 但是,搜索 grep fgrep 之类的东西只会崩溃,即使内存不足也是如此。
整数位于 txt 文件中的一行。
那么,无论需要多长时间,我现在如何在那里找到文本,最好不要将其拆分为小文件或类似的东西,以便文件保持不变。
以及将来如何在那里快速搜索,例如制作一个用于搜索的网站,并且已经在那里使用 sql / 分区到文件中,或者其他东西,在这里建议一些可能会有所帮助的东西。
提前致谢,我尝试了我所说的 grep、fgrepX 和其他一些开源的东西。
答:
如果标准程序不够好,请自己制作。
我在 Rust 中为你写了这个。我之所以选择 Rust,是因为它有一个令人印象深刻的 memchr::memmem 库。它应该相当快。它绝对不会耗尽内存。我希望它没有任何错误。在线搜索如何安装 Rust。使用 编译程序。cargo build --release
fastgrep/src/main.rs:
use std::io::Read;
use std::os::unix::ffi::OsStrExt;
fn main() -> std::io::Result<()> {
const BUF_SIZE: usize = 8192;
let args: Vec<_> = std::env::args_os().collect();
let needle: &[u8] = args[1].as_bytes();
let filename = &args[2];
let finder = memchr::memmem::Finder::new(needle);
let mut file = std::fs::File::open(filename)?;
let mut offset = 0;
let mut buffer: Vec<u8> = vec!();
loop {
let old_len = buffer.len();
buffer.resize(old_len + BUF_SIZE, 0);
let bytes_read = file.read(&mut buffer[old_len..])?;
buffer.truncate(old_len + bytes_read);
// eprintln!("old_len {old_len} bytes_read {bytes_read}");
for offset_in_buffer in finder.find_iter(&buffer) {
println!("{}", offset + offset_in_buffer);
}
if bytes_read == 0 {
break;
}
let advance = buffer.len().saturating_sub(needle.len() - 1);
// eprintln!("advance {advance}");
offset += advance;
buffer = buffer[advance..].to_vec();
}
Ok(())
}
fastgrep/Cargo.toml:
[package]
name = "fastgrep"
version = "0.1.0"
edition = "2021"
[dependencies]
memchr = "2.6.4"
Rust playground 示例(硬编码参数):
https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=e2385dbb56bdaee15ba4b827245a19e9
祝你好运!请写评论告诉我它是否有效!
如果标准程序不够好,请尝试用于搜索二进制文件的程序。
您的文件并不是真正的二进制文件,其中一些程序可能希望您以十六进制形式编写指针,但至少它们不会尝试逐行读取输入。
本文 https://www.baeldung.com/linux/binary-files-pattern-search 提到了以下工具:
- bgrep - https://github.com/tmbinc/bgrep 或此叉子:https://github.com/rsharo/bgrep
- BBE - https://bbe-.sourceforge.net/
- GHex (GUI)(但“GHex 将整个文件加载到内存中”)
- 祝福 (GUI) - https://github.com/afrantzis/bless
一些 Linux 发行版有一些用于这些工具的软件包。
grep
在搜索其中的文本之前,将整个文件读入内存。你可以将 GNU awk 用于多字符 RS 和 RT,并告诉它一次搜索或任何你喜欢的字符数:1000
gawk -v RS='.{0,1000}' 'RT ~ /regexp/' file
但是,您需要考虑匹配的字符串可能会在 1000 个字符的边界上被破坏,因此请确保每条记录都足够大以容纳匹配的字符串,并且每次测试连接的 2 条记录,例如:
gawk -v RS='.{0,1000}' '(prev RT) ~ /regexp/; {prev=RT}' file
以上内容显然是未经测试的,因为问题中没有样本输入/输出供我们测试,而且 OP 没有告诉我们如果/当正则表达式匹配等,他们想要输出什么,所以这一切都留给他们弄清楚,但也许像这样的东西只是打印或与适当的退出状态一起是所需要的:found
not found
$ echo '00007770000' | gawk -v RS='.{0,3}' '(prev RT) ~ /777/{f=1; exit} {prev=RT} END{print (f ? "" : "not ") "found"; exit !f}'
found
$ echo $?
0
$ echo '00007770000' | gawk -v RS='.{0,3}' '(prev RT) ~ /333/{f=1; exit} {prev=RT} END{print (f ? "" : "not ") "found"; exit !f}'
not found
$ echo $?
1
正如@dave_thompson_085在评论中建议的那样,鉴于 OP 输入不包含空格(否则会将它们从它输出的每个块的末尾剥离),以下内容将使用 POSIX 工具工作:dd
dd cbs=1000 conv=unblock if=file | awk '(prev $0) ~ /regexp/; {prev=$0}'
而且,现在我考虑一下,无论是否存在空格,这也适用于 POSIX 工具:
fold -b -w 1000 file | awk '(prev $0) ~ /regexp/; {prev=$0}'
从技术上讲,输入应该以换行符结尾,或者每个 POSIX 的未定义行为,但它可能会起作用,如果输入不以换行符结尾,并且您的盒子上的版本需要换行符,您可以随时添加它:fold
fold
{ cat file; printf '\n'; } | fold -b -w 1000 | awk '(prev $0) ~ /regexp/; {prev=$0}'
我使用参数,因此它的输入行长度不限于LINE_MAX但是我们处理的是字节而不是字符,如果您的输入包含多字节字符,则这些字符可能会表现得不受欢迎。-b
fold
评论
dd cbs=1000 conv=unblock | awk '{prev $0)~/regexp/; {prev=$0}'
一个 Perl 单行代码:
perl -MSys::Mmap -E'mmap($data,0,PROT_READ,MAP_SHARED,STDIN);while(($ix=index$data,$ARGV[0],($ix//-1)+1)>=0){say"match at $ix"}' word </long/file/path
它需要安装 Perl 模块 Sys::Mmap。在 Debian(及其衍生产品)中,它以 package 的形式提供。libsys-mmap-perl
Sys::Mmap
允许将文件的内容包装为 Perl 标量。在后台,它使用 mmap(2)。但是这些标量的使用很棘手,因为一些 perl 内置函数可能会尝试复制它们,从而导致内存不足错误。
例如,我无法在不触发其副本的情况下在创建的标量上运行正则表达式。另一方面,在单行中使用,有效!Sys::Mmap
index
评论