对文本文件的特定编辑(awk?)

我有一个看起来像的文件

TITLE 1.000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 UU XX C 0.2000000000000028 0.2000000000000028 0.2000000000000028 0.2967599999999990 0.0641000000000034 0.1551499999999990 0.1033699999999982 0.3361099999999979 0.244990000000001 

我需要一个脚本来修改底部数字块( C到30以下比原始值少。有这样的方法吗?

到目前为止,我得到的最好的是

 $ awk '{if(NR>1){for(i=2;i<=NF;i++){$(i)=$(i)-10;}}print;}' data.txt | column -t 

但是,这是来自互联网,我不知道如何自己操纵它达到预期的效果。 然而,这不会打印/覆盖当前data.txt ,这就是我想要的。

谢谢您的帮助!

这是我的awk版本:

awk '/^C/,0 {for (i=1;i<=NF;i++) { if ( $i != "C" ) printf "%.16f ",$i-30.0000};print"\n" }' data.txt

这里我们从C字符到文件末尾得到所有东西,在每一列中减去30,添加换行符,然后重复该过程。添加if语句以避免从C中减去30,显然。

输出是这样的:

  46)serg@ubuntu[/home/xieerqi] >_ awk '/^C/,0 {for (i=1;i<=NF;i++) { if ( $i != "C" ) printf "%.16f ",$i-30.0000};print"\n" }' data.txt -29.7999999999999972 -29.7999999999999972 -29.7999999999999972 -29.7032400000000010 -29.9358999999999966 -29.8448500000000010 -29.8966300000000018 -29.6638900000000021 -29.7550099999999986 

这可以在原始文件中替换; 或者我们总是可以尝试使用BEGIN {}语句在C之前打印内容

鉴于data.awk如下:

 { if (matched) { for (i = 1; i <= NF; i++) { $(i) = 30.0 - $(i) } } print } /^C/ { matched = 1 } BEGIN { CONVFMT = "%.20f" } 

你得到:

 $ awk -f data.awk data.txt TITLE 1.000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 UU XX C 29.79999999999999715783 29.79999999999999715783 29.79999999999999715783 29.70324000000000097543 29.93589999999999662350 29.84485000000000098908 29.89663000000000181444 29.66389000000000208956 29.75500999999999862666 

它显然有输入的精度问题。 所以你可能想调用bc命令进行实际计算(它支持任意精度):

 { if (matched) { for (i = 1; i <= NF; i++) { cmd = "echo 30.0 - " $(i) " | bc" cmd | getline $(i) close(cmd) } } print } /^C/ { matched = 1 } 

结果:

 TITLE 1.000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 UU XX C 29.7999999999999972 29.7999999999999972 29.7999999999999972 29.7032400000000010 29.9358999999999966 29.8448500000000010 29.8966300000000018 29.6638900000000021 29.755009999999999 

要使用结果覆盖data.txt ,通常需要将其写入另一个文件,然后将其重命名为原始文件。

 $ awk -f data.awk data.txt > data.txt.out $ mv data.txt.out data.txt 

或者在moreutils中使用sponge

 $ sudo apt-get install moreutils $ awk -f data.awk data.txt | sponge data.txt 

使用python

 #!/usr/bin/env python2 import decimal with open('/path/to/data.txt') as f: for line in f: if line.rstrip() == 'C': print line.rstrip() break else: print line.rstrip() for line in f: print '\t'.join(['{0:.16f}'.format(decimal.Decimal(30 - float(part))) for part in line.rstrip().split()]) 

输出:

 TITLE 1.000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 UU XX C 29.7999999999999972 29.7999999999999972 29.7999999999999972 29.7032400000000010 29.9358999999999966 29.8448500000000010 29.8966300000000018 29.6638900000000021 29.7550099999999986 
  • 每次python读取文件的一行时,指针增加1指向下一行,我们利用它来读取并打印到只包含C的行。

  • 对于C之后的行,我们将行拆分为line.rstrip().split() ,然后从30减去每个部分以获得所需的结果。

  • 为了获得结果浮点数的精度,我们使用了decimal模块。

我无法为这个问题提供一个好的解决方案,但我会尝试深入描述问题是什么,我将提供一个部分解决方案。

问题

机器上的浮点数具有有限的精度:简而言之,只有有限的浮点数子集[每个数量级]是可表示的。

机器上的浮点数紧跟着标准化符号± significand * base ^ exponent (其中base =表示基数, significand =任何实数> 0且<=表示基数,其中exponent =数量级):例如,在遵循IEEE 754标准的32位机器上,使用第一位表示符号表示单精度浮点数,后面的8位表示数量级,最后23位表示有效数字虽然使用第一位表示双精度浮点数来表示符号,但是后面的11位用于表示数量级,最后52位用于表示有效数(基数,始终为2,未表示) 。 为此,必须始终使用23位(使用单精度)或使用52位(使用双精度)来表示数字的有效数。

这种在固定数量的比特上表示浮点数的方式的特性是,每个数量级的可表示有效数的数量总是相同的,具有相同数量级的可表示浮点数之间的平均“距离”增加为两者的数量级增加。

对于上述情况,第一个问题是如果浮点数的标准化符号的有效数不在可表示的有效数字的有限集合中,则它四舍五入到可表示的最接近(更高或更低)有效数。

说到以相同数量级表示的数字,第二个问题是即使浮点数可以精确表示,向其添加/减去另一个[可精确表示的]浮点数也可能导致不能精确表示的浮点数,其有效数将四舍五入到最接近(更高或更低)的有效数字。

最后,谈到以不同数量级表示的数字,第三个问题(主要是由于CPU架构)是为了能够在以不同数量级表示的浮点数之间执行加法/减法,数字需要首先使用相同的数量级表示; 这意味着需要增加最小的一个数量级,并且(为了平衡这个)其有效数需要向右移动,结果丢失的比特数超过可用的23/52; 如果这还不够,那么一旦加/减,可能会导致其数量级存在显着差异的浮点数正好在具有最高绝对值的数字中,这对于已经陈述的问题(没有足够的差异来执行非可表示的有效数字向上/向下到不同的更高/更低可表示的有效数字)并且随着两个数字的数量级进一步发散而越来越差。

所有这些的含义是:您永远不会确定使用浮点数学得到准确的结果,但是这可以通过使用更高精度的表示来减轻。

部分解决方案

对于上述情况,这些awk的结果并不精确; 这可以通过在printf命令中使用双精度来缓解,但这不受支持。


在第一行匹配C之后,这将使每行中前3个以空格分隔的数字的值减少30 ,从而保持数字的格式。 由于Ubuntu中包含的awk版本不支持就地编辑,因此您必须使用awk并使用bash>运算符将其stdout重定向到文件或使用gawk (GNU awk )> = 4.10.0 ;

使用awk

 awk 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}' data.txt > data_processed.txt 

使用gawk (GNU awk )> = 4.10.0

 gawk -i inplace 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}' data.txt 
  • NR==1, $0=="C"; :选择并打印第一个和第一个匹配C之间的所有记录;
  • $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30} :选择所有记录在第一个匹配的C和最后一个匹配的C之间打印每个所选记录的第1,第2和第3个字段,不匹配C双空格分隔,减少30保持原始数字的格式;

样本输出:

 ~/tmp$ cat data.txt TITLE 1.000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 UU XX C 0.2000000000000028 0.2000000000000028 0.2000000000000028 0.2967599999999990 0.0641000000000034 0.1551499999999990 0.1033699999999982 0.3361099999999979 0.244990000000001 ~/tmp$ awk 'NR==1, $0=="C"; $0=="C", 0 {if ($0!="C") printf "%.16f %.16f %.16f\n", $1-30, $2-30, $3-30}' data.txt TITLE 1.000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 0.0000000000000000 0.0000000000000000 0.0000000000000000 10.0000000000000000 UU XX C -29.7999999999999972 -29.7999999999999972 -29.7999999999999972 -29.7032400000000010 -29.9358999999999966 -29.8448500000000010 -29.8966300000000018 -29.6638900000000021 -29.7550099999999986