在文件的每一行前面加上前导零

Prepend leading zeros to each line of a file

提问人:Monkey05connor 提问时间:10/17/2023 最后编辑:Timur ShtatlandMonkey05connor 更新时间:10/19/2023 访问量:251

问:

我有一个文件,如下所示:

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 数字 零填充

评论

0赞 Ed Morton 10/18/2023
提供真正具有代表性的样本输入总是好的,而不仅仅是 等,这样您就可以向我们展示您的输入的真实示例,其中包括未雨绸缪的情况,例如文本的该部分中的 s。line1:

答:

7赞 wayofthepie 10/17/2023 #1

有很多方法,一种方法是使用awk

awk -F":" '{OFS=FS; $1 = sprintf("%05d", $1); print}' "${filename}"

分解一下:

  • -F":"将字段分隔符设置为“:”,会将每行拆分为列。awk:
  • OFS=FS将输出字段分隔符设置为字段分隔符,这实质上是在输出时将“:”放回每一行。
  • $1 = sprintf("%05d", $1)将第一列 设置为本身填充 0 且长度为 5。$1
  • print打印行。

评论

1赞 wayofthepie 10/17/2023
这是对原始的、未经编辑的问题的回应,该问题没有提到 awk 而是使用 printf。反正会离开这里。
3赞 dawg 10/17/2023 #2

您可以执行以下操作:

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

评论

1赞 Thor 10/17/2023
甚至更多打高尔夫球awk '$1=sprintf("%05d",$1)' FS=: OFS=:
0赞 RARE Kpop Manifesto 10/19/2023
@dawg @Thor :echo '9992315351235323253252317:line9' | gawk '$1=sprintf("%05d", $1)' FS=: OFS=: ====> 9992315351235322445824000:line9
1赞 Thor 10/17/2023 #3

coreutils 替代方案:

paste -d: <(printf "%05d\n" $(cut -d: -f1 infile)) <(cut -d: -f2- infile)

评论

0赞 Ed Morton 10/18/2023
如果你做到了,那么我认为即使 ,等等,它也会起作用。 包含 s。-f2--f2line1:
3赞 Freeman 10/17/2023 #4

也看看(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:valuep=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
2赞 Timur Shtatland 10/17/2023 #5

使用以下 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

正则表达式使用以下修饰符: :
计算为表达式
/eREPLACEMENTs/PATTERN/REPLACEMENT/

^:行首。
:一个或多个数字。匹配被捕获到变量中,我们稍后在 中使用。
:返回一个字符串,其中捕获的数字用 s 填充以给出长度数字。
\d+$&sprintfsprintf "%05d", $&$&05

另请参阅:

4赞 Ed Morton 10/18/2023 #6

使用任何 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
4赞 RavinderSingh13 10/18/2023 #7

在 中添加另一种执行此操作的方法。将字段分隔符和输出字段分隔符设置为,并根据需要向第一个字段添加空格以使其仅为 5,然后将每个空格替换为 。awk:0

awk -F':' -v OFS=':' '{$1=sprintf("%5s",$1);gsub(/ /,"0",$1)} 1' Input_file
1赞 KingWealth 10/18/2023 #8

这是一个 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>

评论

0赞 Freeman 10/18/2023
您的脚本无法正常工作!输出是00001:post 00014:post 00135:post 00015:post
0赞 camille 10/20/2023
答案应该解决被问到的具体问题---关于awk的问题,所以你应该用awk而不是Python来回答
0赞 Gustavo Castro 10/18/2023 #9

代码中的错误是您的打印 FNR 等于记录数和 $0(所有字段)。如果设置字段分隔符 (-F=“:”),则字段 $1 的值为加零,$2 是第二个具有行值的字段。 因此,将 $1(与 %05d 相加零)打印到 printf 语句中,将 $2(行值)

 awk -F"\:" '{printf "%05d:%s\n", $1, $2}' file

为带有正则表达式 “:” 的字符串添加 “\”

评论

0赞 Freeman 10/18/2023
如果 等包含 s 无法正常工作!line1:
1赞 RARE Kpop Manifesto 10/19/2023 #10

更新了简化版本,保留了 : 的链,同时保留了任何输入长度的精度

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
               |             

标记行处或其右边的所有数字均已损坏。 当然可以减轻这种风险,但是为什么一开始就不需要降低风险呢?%05dbigint