提问人:Monkey05connor 提问时间:10/17/2023 最后编辑:Timur ShtatlandMonkey05connor 更新时间:10/19/2023 访问量:251
在文件的每一行前面加上前导零
Prepend leading zeros to each line of a file
问:
我有一个文件,如下所示:
1:line1
14:line2
135:line3
15:line4
我需要在每行前面加上前导零,使其看起来像这样:
00001:line1
00014:line2
00135:line3
00015:line4
在 Linux 中有一种简单的方法可以做到这一点吗?
我试过使用
awk '{printf "%05d:%s\n", FNR, $0}' file
但这输出:
00001:1:line1
00002:14:line2
00003:135:line3
00004:15:line4
我应该注意,我没有写这个命令,我是从谷歌那里得到的,并不真正了解它是如何工作的
答:
有很多方法,一种方法是使用awk
awk -F":" '{OFS=FS; $1 = sprintf("%05d", $1); print}' "${filename}"
分解一下:
-F":"
将字段分隔符设置为“:”,会将每行拆分为列。awk
:
OFS=FS
将输出字段分隔符设置为字段分隔符,这实质上是在输出时将“:”放回每一行。$1 = sprintf("%05d", $1)
将第一列 设置为本身填充 0 且长度为 5。$1
print
打印行。
评论
您可以执行以下操作:
awk 'BEGIN{FS=OFS=":"}
{$1=sprintf("%05d", $1)} 1' file
指纹:
00001:line1
00014:line2
00135:line3
00015:line4
从评论中,一个很酷的版本:
awk '$1=sprintf("%05d", $1)' FS=: OFS=: file
# same
评论
awk '$1=sprintf("%05d",$1)' FS=: OFS=:
echo '9992315351235323253252317:line9' | gawk '$1=sprintf("%05d", $1)' FS=: OFS=:
====> 9992315351235322445824000:line9
coreutils 替代方案:
paste -d: <(printf "%05d\n" $(cut -d: -f1 infile)) <(cut -d: -f2- infile)
评论
-f2-
-f2
line1
:
也看看(thnx 到 @EdMorton)
awk -F':' '{
p = index($0, ":")
tag = substr($0, 1, p-1)
val = substr($0, p+1)
# or tag=val=$0; sub(/:.*/,"",tag); sub(/[^:]+:/,"",val)
printf "%05d:%s\n", tag, val
}' input.txt
任何时候你有对,如果你不知道并完全控制哪个字符值可以包含,那么做或类似的事情比 更安全。tag:value
p=index($0,":"); tag=substr($0,1,p-1); val=substr($0,p+1)
tag=val=$0; sub(/:.*/,"",tag); sub(/[^:]+:/,"",val)
tag=$1; val=$2
你也可以用 ruby 解决这个问题:
ruby -ne 'puts "%05d:%s" % $_.split(":")' input.txt
或者 perl
perl -pe 's/(\d+):/sprintf "%05d:", $1/e' input.txt
输出
00001:line1
00014:line2
00135:line3
00015:line4
使用以下 Perl 单行代码:
perl -lpe 's{^\d+}{sprintf "%05d", $&}e;' infile > outfile
要就地更改文件,请执行以下操作:
perl -i.bak -lpe 's{^\d+}{sprintf "%05d", $&}e;' infile
Perl 单行代码使用以下命令行标志: :
告诉 Perl 以内联方式查找代码,而不是在文件中查找代码。
:一次循环一行输入,默认分配给输入。在每次循环迭代后添加。
:在内联执行代码之前,剥离输入行分隔符(默认在 *NIX 上),并在打印时附加它。
:就地编辑输入文件(覆盖输入文件)。在覆盖之前,通过在其名称后附加扩展名来保存原始文件的备份副本。如果要跳过编写备份文件,只需使用并跳过扩展名即可。-e
-p
$_
print $_
-l
"\n"
-i.bak
.bak
-i
正则表达式使用以下修饰符: :
计算为表达式/e
REPLACEMENT
s/PATTERN/REPLACEMENT/
^
:行首。
:一个或多个数字。匹配被捕获到变量中,我们稍后在 中使用。
:返回一个字符串,其中捕获的数字用 s 填充以给出长度数字。\d+
$&
sprintf
sprintf "%05d", $&
$&
0
5
另请参阅:
perldoc perlrun
:如何执行 Perl 解释器:命令行开关perldoc perlre
: Perl 正则表达式(正则表达式)perldoc perlrequick
:Perl 正则表达式快速入门sprintf
使用任何 awk:
$ awk '{p=index($0,":"); printf "%05d%s\n", substr($0,1,p-1), substr($0,p)}' file
00001:line1
00014:line2
00135:line3
00015:line4
在 中添加另一种执行此操作的方法。将字段分隔符和输出字段分隔符设置为,并根据需要向第一个字段添加空格以使其仅为 5,然后将每个空格替换为 。awk
:
0
awk -F':' -v OFS=':' '{$1=sprintf("%5s",$1);gsub(/ /,"0",$1)} 1' Input_file
这是一个 python 脚本。 在这里,我们只是拆分行并将数字填充到所需的长度。
def format_data(filename: str, int_length: int):
new_lines = []
if not os.path.exists(filename):
print("No file found!")
return 1
with open(filename, "r") as fileobj:
for line in fileobj.readlines():
try:
pre, post = line.split(":")
new_lines.append(f"{pre.zfill(int_length)}:post")
except Exception:
print(f"Some error")
new_lines.append(line)
write_filename = f"updated_{filename}"
with open(write_filename, "w") as fileobj:
fileobj.writelines([string + '\n' for string in new_lines])
print(f"Saved updated file {write_filename}")
return 0
根据需要使用文件名和数字的总长度调用函数
format_data("sample.txt", 5)
输出文件将写入同一目录。updated_<filename>
评论
00001:post 00014:post 00135:post 00015:post
代码中的错误是您的打印 FNR 等于记录数和 $0(所有字段)。如果设置字段分隔符 (-F=“:”),则字段 $1 的值为加零,$2 是第二个具有行值的字段。 因此,将 $1(与 %05d 相加零)打印到 printf 语句中,将 $2(行值)
awk -F"\:" '{printf "%05d:%s\n", $1, $2}' file
为带有正则表达式 “:” 的字符串添加 “\”
评论
line1
:
更新了简化版本,保留了 : 的链,同时保留了任何输入长度的精度:
echo '
2:line1
00123:line
::::::::line3
00000000000005923555555555555555555555555877777777:line9' |
gtee >( gcat -b >&2; ) |
mawk '$!NF = substr("00000",__ = index($(NF += OFS = _),
":"), (__ < 6) * 5) $_' FS='^0+'
1 2:line1
2 00123:line2
3 ::::::::line3
4 00000000000005923555555555555555555555555877777777:line9
00002:line1
00123:line2
00000::::::::line3
5923555555555555555555555555877777777:line9
======================================================
它应该分为 3 种场景:
如果已经完全 5 个,则跳过子字符串
如果前导过剩部分较长,则以精确保留的方式进行零修剪。
较短时将垫子设置为 5
echo '
1:line1
14:line2
135:line3
15:line4
00000003523532:line5' |
mawk '{ gsub(/::+/, ":") } ! (_ = 6-index($__, ":")) ||
$!NF = _<-_ ? substr($__, match($__, /[^0]*.....:/)) \
: sprintf("%.*d",_,__) $__'
00001:line1
00014:line2
00135:line3
00015:line4
3523532:line5
- 当两者都不需要时,利用利用两个字符串到数字然后执行另一个数字到字符串的转换的解决方案,
%05d
使用较长的输入可能会改变输入本身::%05d
echo '9992315351235323253252317:line9' |
gawk '{p=index($0,":"); printf "%05d%s\n", substr($0,1,p-1), substr($0,p)}'
|
9992315351235322445824000:line9
|->
9992315351235323253252317:line9
|
标记行处或其右边的所有数字均已损坏。 当然可以减轻这种风险,但是为什么一开始就不需要降低风险呢?%05d
bigint
评论
line1
: