在 AWK 中编辑带有空格的浮动特定点字段 Preserving/Padding?
Edit Floating Specific Point Field Preserving/Padding w/ Whitespace in AWK?
我有一个包含以下行的文件:
CH1 12.30 4.800 12 !
我想用 [0.0,1.0)
上选定的浮点标量缩放的一些等效项替换特定字段...比如 </code>。但是,我想保留相同的小数位数,并进一步在前端填充空格以保持原始长度。</p>
<p>我认为 <code>awk
中的某些组合 len
/gsub
/printf
可以实现这一点。
举个我目前尝试过的例子:
scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(/[0-9]*\.[0-9]*/,*sc,);} {print;}'
输出:
CH1 0 4.800 12 !
输出:
正确输出缩放的 #,但空格不仅从字段 </code> 中删除,而且从整行中删除。</p>
<p><code>scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(//,*sc,[=21=]);} {print;}'
输出:
CH1 12.30 4.800 12 !
备注:
什么也没做!输出不变。
假设:
- 字段
</code> 和 <code>
可能相同,但我只想更改字段 </code>.</li>
<li>字段 <code>
仅包含字母数字字符。
- 字段
</code> 和 <code>
是具有任意小数位数的浮点数,通常小数位数在 [1,4]
范围内。整个部分不超过3位数..
- 字段
</code> 是 <code>[8,99]
. 范围内的整数
- 字段
之后的任何内容都是注释,可能包含特殊字符。
在搜索类似问题时,我遇到了一些与保留空格有关的问题,这些问题给了我一些想法……但我的有点不同,因为我实际上想添加空格,以保留小数位有效锁定在线的同一位置,使目标文件中的用户格式保持良好。
gsub(//,...)
表达式失败,因为 //
正在寻找文字 </code> 字符串,而不是字段 2 中的任何内容。(并且 <code>gsub
是矫枉过正,因为我们只改变一个实例,所以简单的 sub
就足够了,但是 gsub
在这里是无害的。)
我们可以只使用 </code>(没有斜杠,尽管它将被视为正则表达式而不是文字字符串):</p>
<pre><code>$ scalar=0.00; echo 'CH1 12.30 4.800 12 !' |
awk -v sc=$scalar '/CH1/{gsub(,*sc);} {print;}'
CH1 0 4.800 12 !
这也失去了小数位的东西,所以仍然不是我们想要的,但表明你的方法是可行的。
鉴于sprintf()
可以根据"%5.2f"
这样的格式指令生成一个字符串(这是我们想要得到的12.30
),我们需要做的就是图得出字段的总长度 </code> 和小数部分的长度(在 <code>.
之后),这很容易使用 split
和 length
。构建替换字符串甚至比乍看起来更容易,因为我们可以使用 *
来提取整数参数,而不是文字 5
和 2
。因此:
$ cat foo.sh
#! /bin/sh
scalar=0.00
echo 'CH1 12.30 4.800 12 !'
echo 'CH1 12.30 4.800 12 !' |
awk -v sc=$scalar '
~ /[0-9]*\.[0-9]*/ {
split(, parts, /\./)
ofraclen = length(parts[2])
repl = sprintf("%*.*f", length(), ofraclen, * sc)
sub(/[0-9]*\.[0-9]*/, repl)
}
{print}
'
$ sh foo.sh
CH1 12.30 4.800 12 !
CH1 0.00 4.800 12 !
我输入了额外的 echo
以便我们可以看到字段仍然排成一行。我将匹配条件更改为 ~ ...
以保证 </code> 将正确拆分。我们将其拆分为整数和小数部分,获取小数部分的长度,生成替换字符串,然后在(第一次)出现浮点数时使用 <code>sub
(安全当且仅当字段 </code> 从不匹配,没有 $1 匹配的测试,如果是这样,我们将 <code>sub
匹配错误。
(我其实很喜欢每条语句后面的分号,但我把它们都去掉了,因为它们不是严格要求的。另外,大部分临时变量都可以去掉,只保留 parts
,但是结果将很难理解。)
这是在对某些字段进行操作后从输入中再现填充的一般方法:
$ cat tst.awk
NR==1 {
# Find the width of each space-padded, right-aligned field:
rec = [=10=]
for (i=1; i<=NF; i++) {
match(rec,/[^[:space:]]+/)
w[i] = RSTART - 1 + RLENGTH
rec = substr(rec,w[i]+1)
}
# Find the precision of the target field:
match(,/\..*/)
p = RLENGTH - 1
}
{
# print the original just for comparison
print
# do the math:
= sprintf("%.*f", p, * scalar)
# print the updated record:
for (i=1;i<=NF;i++) {
printf "%*s", w[i], $i
}
print ""
}
.
$ awk -v scalar=0 -f tst.awk file
CH1 12.30 4.800 12 !
CH1 0.00 4.800 12 !
$ awk -v scalar=0.5 -f tst.awk file
CH1 12.30 4.800 12 !
CH1 6.15 4.800 12 !
$ awk -v scalar=9 -f tst.awk file
CH1 12.30 4.800 12 !
CH1 110.70 4.800 12 !
无论标量的值是多少或您要更改哪个浮点字段(如果需要,也可以轻松调整以适用于十进制字段)以及无论
的值是多少,以上内容都将起作用。
我有一个包含以下行的文件:
CH1 12.30 4.800 12 !
我想用 [0.0,1.0)
上选定的浮点标量缩放的一些等效项替换特定字段...比如 </code>。但是,我想保留相同的小数位数,并进一步在前端填充空格以保持原始长度。</p>
<p>我认为 <code>awk
中的某些组合 len
/gsub
/printf
可以实现这一点。
举个我目前尝试过的例子:
scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(/[0-9]*\.[0-9]*/,*sc,);} {print;}'
输出:
CH1 0 4.800 12 !
输出:
正确输出缩放的 #,但空格不仅从字段 </code> 中删除,而且从整行中删除。</p>
<p><code>scalar=0.00; echo 'CH1 12.30 4.800 12 !' | awk -v sc=$scalar '/CH1/{gsub(//,*sc,[=21=]);} {print;}'
输出:
CH1 12.30 4.800 12 !
备注: 什么也没做!输出不变。
假设:
- 字段
</code> 和 <code>
可能相同,但我只想更改字段</code>.</li> <li>字段 <code>
仅包含字母数字字符。 - 字段
</code> 和 <code>
是具有任意小数位数的浮点数,通常小数位数在[1,4]
范围内。整个部分不超过3位数.. - 字段
</code> 是 <code>[8,99]
. 范围内的整数
- 字段
之后的任何内容都是注释,可能包含特殊字符。
在搜索类似问题时,我遇到了一些与保留空格有关的问题,这些问题给了我一些想法……但我的有点不同,因为我实际上想添加空格,以保留小数位有效锁定在线的同一位置,使目标文件中的用户格式保持良好。
gsub(//,...)
表达式失败,因为 //
正在寻找文字 </code> 字符串,而不是字段 2 中的任何内容。(并且 <code>gsub
是矫枉过正,因为我们只改变一个实例,所以简单的 sub
就足够了,但是 gsub
在这里是无害的。)
我们可以只使用 </code>(没有斜杠,尽管它将被视为正则表达式而不是文字字符串):</p>
<pre><code>$ scalar=0.00; echo 'CH1 12.30 4.800 12 !' |
awk -v sc=$scalar '/CH1/{gsub(,*sc);} {print;}'
CH1 0 4.800 12 !
这也失去了小数位的东西,所以仍然不是我们想要的,但表明你的方法是可行的。
鉴于sprintf()
可以根据"%5.2f"
这样的格式指令生成一个字符串(这是我们想要得到的12.30
),我们需要做的就是图得出字段的总长度 </code> 和小数部分的长度(在 <code>.
之后),这很容易使用 split
和 length
。构建替换字符串甚至比乍看起来更容易,因为我们可以使用 *
来提取整数参数,而不是文字 5
和 2
。因此:
$ cat foo.sh
#! /bin/sh
scalar=0.00
echo 'CH1 12.30 4.800 12 !'
echo 'CH1 12.30 4.800 12 !' |
awk -v sc=$scalar '
~ /[0-9]*\.[0-9]*/ {
split(, parts, /\./)
ofraclen = length(parts[2])
repl = sprintf("%*.*f", length(), ofraclen, * sc)
sub(/[0-9]*\.[0-9]*/, repl)
}
{print}
'
$ sh foo.sh
CH1 12.30 4.800 12 !
CH1 0.00 4.800 12 !
我输入了额外的 echo
以便我们可以看到字段仍然排成一行。我将匹配条件更改为 ~ ...
以保证 </code> 将正确拆分。我们将其拆分为整数和小数部分,获取小数部分的长度,生成替换字符串,然后在(第一次)出现浮点数时使用 <code>sub
(安全当且仅当字段 </code> 从不匹配,没有 $1 匹配的测试,如果是这样,我们将 <code>sub
匹配错误。
(我其实很喜欢每条语句后面的分号,但我把它们都去掉了,因为它们不是严格要求的。另外,大部分临时变量都可以去掉,只保留 parts
,但是结果将很难理解。)
这是在对某些字段进行操作后从输入中再现填充的一般方法:
$ cat tst.awk
NR==1 {
# Find the width of each space-padded, right-aligned field:
rec = [=10=]
for (i=1; i<=NF; i++) {
match(rec,/[^[:space:]]+/)
w[i] = RSTART - 1 + RLENGTH
rec = substr(rec,w[i]+1)
}
# Find the precision of the target field:
match(,/\..*/)
p = RLENGTH - 1
}
{
# print the original just for comparison
print
# do the math:
= sprintf("%.*f", p, * scalar)
# print the updated record:
for (i=1;i<=NF;i++) {
printf "%*s", w[i], $i
}
print ""
}
.
$ awk -v scalar=0 -f tst.awk file
CH1 12.30 4.800 12 !
CH1 0.00 4.800 12 !
$ awk -v scalar=0.5 -f tst.awk file
CH1 12.30 4.800 12 !
CH1 6.15 4.800 12 !
$ awk -v scalar=9 -f tst.awk file
CH1 12.30 4.800 12 !
CH1 110.70 4.800 12 !
无论标量的值是多少或您要更改哪个浮点字段(如果需要,也可以轻松调整以适用于十进制字段)以及无论 的值是多少,以上内容都将起作用。