如何仅用一行搜索 104GB 文件中的特定文本?

How to search specific text in 104GB file with only one line?

提问人:sdfsdf 提问时间:10/8/2023 最后编辑:sdfsdf 更新时间:10/28/2023 访问量:130

问:

我刚刚使用 y-cruncher 计算完 PI 数的 1110 亿位数字,还有 104GB 的文件。

我遇到了一个问题。

我想玩一玩,试着在那里搜索名称、值、标题等,那是某个数字序列。 但是,搜索 grep fgrep 之类的东西只会崩溃,即使内存不足也是如此。

整数位于 txt 文件中的一行。

那么,无论需要多长时间,我现在如何在那里找到文本,最好不要将其拆分为小文件或类似的东西,以便文件保持不变。

以及将来如何在那里快速搜索,例如制作一个用于搜索的网站,并且已经在那里使用 sql / 分区到文件中,或者其他东西,在这里建议一些可能会有所帮助的东西。

提前致谢,我尝试了我所说的 grep、fgrepX 和其他一些开源的东西。

Linux 搜索 grep

评论

0赞 Mark Setchell 10/8/2023
请使用国际公认的国际单位制单位。技嘉是“GB”。千兆位是“Gb”。未指定前缀“g”。
1赞 sdfsdf 10/8/2023
准备好了,我认为现在是正确的,104GB,所以 104 GB
0赞 sdfsdf 10/8/2023
是的,“内存不足”
0赞 sdfsdf 10/8/2023
我砰,因为我把所有数字都放在一行里
1赞 Ed Morton 10/11/2023
@dimich是什么让您认为文件不是文本?

答:

0赞 Tomi 10/9/2023 #1

如果标准程序不够好,请自己制作。

我在 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

祝你好运!请写评论告诉我它是否有效!

1赞 Tomi 10/9/2023 #2

如果标准程序不够好,请尝试用于搜索二进制文件的程序。

您的文件并不是真正的二进制文件,其中一些程序可能希望您以十六进制形式编写指针,但至少它们不会尝试逐行读取输入。

本文 https://www.baeldung.com/linux/binary-files-pattern-search 提到了以下工具:

一些 Linux 发行版有一些用于这些工具的软件包。

4赞 Ed Morton 10/11/2023 #3

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 没有告诉我们如果/当正则表达式匹配等,他们想要输出什么,所以这一切都留给他们弄清楚,但也许像这样的东西只是打印或与适当的退出状态一起是所需要的:foundnot 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 的未定义行为,但它可能会起作用,如果输入不以换行符结尾,并且您的盒子上的版本需要换行符,您可以随时添加它:foldfold

{ cat file; printf '\n'; } | fold -b -w 1000 | awk '(prev $0) ~ /regexp/; {prev=$0}'

我使用参数,因此它的输入行长度不限于LINE_MAX但是我们处理的是字节而不是字符,如果您的输入包含多字节字符,则这些字符可能会表现得不受欢迎。-bfold

评论

1赞 dave_thompson_085 10/28/2023
POSIX方式可以是dd cbs=1000 conv=unblock | awk '{prev $0)~/regexp/; {prev=$0}'
0赞 Ed Morton 10/28/2023
@dave_thompson_085是的,只要输入不包含空格,就像在 OP 的情况下一样,这应该可以正常工作。我把它添加到我的回答中,谢谢你的建议
1赞 salva 10/28/2023 #4

一个 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::Mmapindex