如何有效地逐个处理文件的字符

时间:2017-02-11 18:04:17

标签: bash performance sed char iteration

有没有办法逐个字符地迭代文件并根据特定条件有选择地替换字符?

我找到了一种使用while循环和sed实用程序的方法:

while IFS= read -r -N 1 old; do
     ...
     sed -i 's/'$old'/'$new'/g' "$1"
done < "$1"

我认为这种方法对于大文件来说非常慢。

有没有办法可以更有效地实现这一目标?

3 个答案:

答案 0 :(得分:0)

一次加载整个文件会快得多,生成所需的输出然后立即全部写出来。

您可以执行以下操作:

input=$(<"$1")
output=''
for ((i=0; i<${#input}; i++)); do
    old=${input:i:1}
    ...
    output+=$new
done

printf '%s' "$output" > "$1"

答案 1 :(得分:0)

您的方法中有 2个性能杀手

  • 使用 shell循环处理数据。

  • 在该循环的每次迭代中调用外部实用程序(sed

    • 具体而言,没有理由在循环的每次迭代中重写文件。
    • 另外,正如已经指出的那样,您在每次迭代中都替换了中正在阅读的文件,这无法正常工作。

<强>替代

  • 根据建议,sed可能就是您所需要的,因为它支持链接多个s///来电(使用;)以及使用字符在给定的调用中设置范围

  • tr是一个有效的实用工具,也支持集合和范围,但它仅限于1对1字符映射(您无法将给定字符映射到多个输出字符)。

如果您确实需要逐个字符处理 使用文本处理实用程序而不是shell代码;例如,awk

$ awk -F'\0' '{ for(i=1;i<=NF;++i) { printf "[%s]", $i }; print "" }' <<<$'abc\ncde'
[a][b][c]
[c][d][e]
  • -F '\0'告诉Awk将每一行分成单个字符,$1代表第一个字符,......和NF反映该行上的字符数

  • 示例命令只包含每个char。在[...]中证明每个字符的处理工作;最后print ""只会发出一个尾随\n

  • 要将其与就地更新(松散地说)相结合,请使用:
    awk -F'\0' '{ ... }' "$1" > "$1.$$" && mv "$1.$$" "$1"

    • 使用 GNU Awk v4.1 +,您还可以使用-i inplace获得与sed -i相同的行为。

答案 2 :(得分:-1)

我终于找到了我想要的东西!我用一个while循环读取行写了下面的代码,一个用于循环读取特定行中的每个字符。这种方式更快,新线路也保持不变!如果这个答案能帮助其他人,我会很高兴的!

#!/bin/bash
lineCounter=1
while IFS='' read -r line || [[ -n "$line" ]]; do
    output=''
    for (( i=0; i<${#line}; i++ )); do

        oldChar=$( printf "${line:$i:1}" )

        ...Compute newChar...

        output+=$newChar

    done
    line2=""
    line2+=$lineCounter
    line2+="s"
    sed -i "$line2/.*/$output/" "$1"
    lineCounter=$((($lineCounter) +1))
done < "$1"