4

例如,给定一行a11b12c22d322 e...break 字段是数字或空格,我们希望将其转换为

a
b
c
d
e
...

sed需要将整行读入内存,对于千兆字节的一行,效率不高,如果我们没有足够的内存就无法完成这项工作。

编辑:

谁能解释一下 grep、tr、Awk、perl 和 python 在读取大文件时如何操作内存?他们一次将什么内容和多少内容读入内存?

4

4 回答 4

6

如果您使用(我相信这是 Linux 上gawk的默认设置),您可以使用该参数指定将多位数字或空格识别为行终止符而不是换行符。awkRS

awk '{print}' RS="[[:digit:]]+| +" file.txt

至于你的第二个问题,所有这些程序都需要读取一些固定数量的字节并在内部缓冲区中搜索行分隔符的想法,以模拟一次读取一行的外观。为防止它在搜索行尾时读取太多数据,您需要更改程序关于什么终止行的想法。

大多数语言允许您这样做,但只允许您指定单个字符。gawk通过允许您指定正则表达式来识别行尾字符,这很容易。这使您不必自己实现固定大小的缓冲区和行尾搜索。

于 2013-01-27T19:01:44.707 回答
4

最快...您可以在 gcc 的帮助下完成,这是一个版本,如果给定,则从给定文件名读取数据,否则从标准输入读取数据。如果这仍然太慢,您可以查看是否可以通过用您自己的缓冲代码替换getchar()putchar()(可能是宏并且应该优化得很好)来使其更快。如果我们想变得更可笑,为了更快,你应该有三个线程,所以内核可以用一个内核复制下一个数据块,而另一个内核处理,第三个内核将处理后的输出复制回内核。

#!/bin/bash

set -e

BINNAME=$(mktemp)

gcc -xc -O3 -o $BINNAME - <<"EOF"
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int sep = 0;

    /* speed is a requirement, so let's reduce io overhead */
    const int bufsize = 1024*1024;
    setvbuf(stdin, malloc(bufsize), _IOFBF, bufsize);
    setvbuf(stdout, malloc(bufsize), _IOFBF, bufsize);
    /* above buffers intentionally not freed, it doesn't really matter here */

    int ch;
    while((ch = getc(stdin)) >= 0) {
        if (isdigit(ch) || isspace(ch)) {
            if (!sep) {
                if (putc('\n', stdout) == EOF) break;
                sep = 1;
            }
        } else {
            sep = 0;
            if (putc(ch, stdout) == EOF) break;
        }
    }

    /* flush should happen by on-exit handler, as buffer is not freed,
       but this will detect write errors, for program exit code */
    fflush(stdout); 

    return ferror(stdin) || ferror(stdout);
}
EOF

if [ -z "$1" ] ; then
  $BINNAME <&0
else
  $BINNAME <"$1"
fi

编辑:我碰巧也查看了 GNU/Linux stdio.h,一些注释:putchar/getchar不是宏,但是putc/getc是,所以使用它们可能是一个轻微的优化,可能避免一个函数调用,更改代码以反映这一点。还添加了检查返回码putc,而在它。

于 2013-01-27T20:16:24.743 回答
3

grep

$ grep -o '[^0-9 ]' <<< "a11b12c22d322 e"
a
b
c
d
e

sed

$ sed 's/[0-9 ]\+/\n/g' <<< "a11b12c22d322 e"
a
b
c
d
e

awk

$ awk 'gsub(/[0-9 ]+/,"\n")' <<< "a11b12c22d322 e"
a
b
c
d
e

我会让你进行基准测试。

于 2013-01-27T18:31:59.580 回答
2

尝试tr

tr -s '[:digit:][:space:]' '\n' <<< "a11b12c22d322e"

这会产生:

a 
b 
c 
d 
e
于 2013-01-27T18:29:57.973 回答