Bash 替换现有文件中浮点数的精度

Bash 替换现有文件中浮点数的精度,bash,Bash,我有大量以下格式的大文件 step 80 1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07 1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07 1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07 1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4

我有大量以下格式的大文件

step 80
1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
step 90
1.54045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
1.16545e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
1.10045e+07 1.10125e+07 1.20345e+07 2.40225e+07 4.30245e+07
...
数字都是不同的(因为我的懒惰,这里有些数字是相同的)

我想更改浮点数的精度。也就是说,我想将上一个文件替换为:

step 80
1.10e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
1.10e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
1.10e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
1.10e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
step 90
1.54e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
1.16e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
1.10e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
1.10e+07    1.10e+07    1.20e+07 2.40e+07   4.30e+07
...
如果所需精度为小数点后2位

如何以一种高效的方式和低内存使用率(也就是说,我不必将整个文件加载到内存中)来实现这一点


最好是使用bash脚本的解决方案。

如果数字的“长”部分是在
'e'
前面一次超过2位的唯一序列,则作为快速而肮脏的解决方案

sed -i "s/\([0-9]\{2\}\)[0-9]*e/\1e/g" <filename>
sed-i“s/\([0-9]\{2\}\)[0-9]*e/\1e/g”
  • “s
    ——搜索
  • /
    --开始“搜索”术语
  • \(
    --开始分组
  • [0-9]
    --数字
  • \{2\}
    --…其中两个(针对不同的输出精度进行编辑)
  • \)
    --端组
  • [0-9]*
    ——后跟任意数字
  • e
    ——后跟
    'e'
  • /
    --结束“搜索”术语,开始“替换”术语
  • \1
    --第一组的内容(前两位数字)
  • e
    --和
    'e'
  • /
    --结束“替换”术语
  • g
    ——每行执行任意次数(“全局”)
-i
选项(GNU扩展名)在文件中进行替换,因此最好先在不使用
-i
的情况下进行尝试,以确保输出符合要求。

$sed's/\(\…\)…e/\1e/g'$ sed 's/\(\...\)...e/\1e/g' < so.txt

step 80
1.10e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
1.10e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
1.10e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
1.10e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
step 90
1.54e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
1.16e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
1.10e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
1.10e+07 1.10e+07 1.20e+07 2.40e+07 4.30e+07
步骤80 1.10e+071.10e+071.20e+072.40e+074.30e+07 1.10e+071.10e+071.20e+072.40e+074.30e+07 1.10e+071.10e+071.20e+072.40e+074.30e+07 1.10e+071.10e+071.20e+072.40e+074.30e+07 步骤90 1.54e+071.10e+071.20e+072.40e+074.30e+07 1.16e+071.10e+071.20e+072.40e+074.30e+07 1.10e+071.10e+071.20e+072.40e+074.30e+07 1.10e+071.10e+071.20e+072.40e+074.30e+07
您可以使用Perl逐行读取文件,并使用以下命令重新格式化数字:

使用awk,您可以执行以下操作:

awk 'NF==5{printf "%.2e\t%.2e\t%.2e\t%.2e\t%.2e\n", $1, $2, $3, $4, $5; next}1' file
如果列数未知,请使用循环:

awk '!/^step/{for(i=1;i<=NF;i++) printf "%.2e%s", $i,i==NF?"\n":"\t";next}1' file

awk'!/^step/{for(i=1;它的格式是这样的,因为其中有选项卡。;-)@DevSolar你是对的!@LutzHorn:非常感谢。你的条件不成立。
步骤可以是任何正整数,大的有许多数字,或小的。@becko应该很容易改进这个答案的匹配。你现在有了一些好的解决方案,使用它们。@becko:添加了
'e'
。应该这样做。
?这很好是的,在
e
前面匹配任何字符都是非常特别的。:-d这不好用…有些条目最终会有更多的数字,
0.000
会被精确的
0
取代,这是不正确的可取的。我不能指出这个问题,我将尝试提出一个简单的例子。这太糟糕了,因为
sed
非常快。我不知道有多少列。而且可能不止5列,所以逐个列出它们不是一个选项。你能改进一下吗?对于我尝试的大文件,循环非常慢。这Works非常完美。唯一的缺点是
sed
速度更快…但它现在工作得并不完美。我怎么能在gnu内部称之为并行。这不起作用
parallel--jobs 20 perl-ape'!/^step/&s/(\s+)/sprintf(%.1e',$1)/ge'{}>{}.fixed:::*
@becko也许可以问一个新问题来解决这个问题。另外,可以省略切换到
perl
-a
(根据编辑)。
awk '!/^step/{for(i=1;i<=NF;i++) printf "%.2e%s", $i,i==NF?"\n":"\t";next}1' file