正则表达式 - 用句点替换行开头的所有空格

时间:2017-10-03 23:31:23

标签: python regex vim awk sed

我不在乎我是否通过vim,sed,awk,python等实现了这一点。我尝试过,无法完成它。

对于这样的输入:

top           f1    f2    f3
   sub1       f1    f2    f3
   sub2       f1    f2    f3
      sub21   f1    f2    f3
   sub3       f1    f2    f3

我想:

top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

然后我想在Excel中加载它(由空格分隔)并且仍然可以查看第一列的层次结构!

我尝试了很多东西,但最终失去了层次结构信息

5 个答案:

答案 0 :(得分:5)

在vim中有两种不同的方法可以做到这一点。

  1. 使用正则表达式:

    :%s/^\s\+/\=repeat('.', len(submatch(0)))
    

    这是相当简单的,但有点冗长。它使用eval寄存器(\=)生成一个'.'字符串,其长度与每行开头的空格数相同。

  2. 使用norm命令:

    :%norm ^hviwr.
    

    这是一个更方便的短命令,虽然它有点难以理解。它直观地选择一行开头的空格,并用点替换整个选择。如果没有前导空格,则命令将在^h上失败,因为光标会尝试移出界限。

    要查看其工作原理,请尝试在包含前导空格的行上键入^hviwr.,以便查看它。

答案 1 :(得分:4)

以此为输入:

$ cat file
top           f1    f2    f3
   sub1       f1    f2    f3
   sub2       f1    f2    f3
      sub21   f1    f2    f3
   sub3       f1    f2    f3

尝试:

$ sed -E ':a; s/^( *) ([^ ])/\1.\2/; ta' file
top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

工作原理:

  • :a

    这会创建一个标签a

  • s/^( *) ([^ ])/\1.\2/

    如果该行以空格开头,则用一个句点替换前导空格中的最后一个空格。

    更详细地说,^( *)匹配除了最后一个之外的所有前导空格并将它们存储在组1中。正则表达式([^ ])(尽管stackoverflow看起来像是什么,但它包含一个空格,后跟([^ ]))匹配一个空格,后跟一个非空白,并将非空白存储在第2组中。

    \1.\2将匹配的文本替换为第1组,后跟一个句点,然后是第2组。

  • ta

    如果替换命令导致替换,则转回标签a并重新尝试。

兼容性:

  1. 以上是在现代GNU sed上测试的。对于BSD / OSX sed,可能需要也可能不需要使用:

    sed -E -e :a -e 's/^( *) ([^ ])/\1.\2/' -e ta file
    

    在古代GNU sed上,需要使用-r代替-E

    sed -r ':a; s/^( *) ([^ ])/\1.\2/; ta' file
    
  2. 以上假设空格是空白的。如果它们是标签,那么你必须决定你的tabstop是什么,并相应地进行替换。

答案 2 :(得分:3)

因为您说 python

#!/usr/bin/env python
import re, sys
for line in sys.stdin:
    sys.stdout.write(re.sub('^ +', lambda m: len(m.group(0)) * '.', line))

(对于每一行,我们用等长的点串'^ +')替换最长的前缀空格len(m.group(0)) * '.'

最终结果:

$ ./dottify.py <file
top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

因为您说 awk

$ awk '{ match($0,/^ +/); p=substr($0,0,RLENGTH); gsub(" ",".",p); print p""substr($0,RLENGTH+1) }' file
top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

(对于每一行我们匹配match空格的最长前缀,用substr提取,用点gsub替换每个空格,然后打印修改后的前缀{{ 1}},后跟输入行的其余部分(pRSTART变量填充在RLENGTH之后,并保持匹配模式的起始位置和长度。)

答案 3 :(得分:3)

在awk中。它一直用一个句点替换第一个空格,而空格只在句点之前:

$ awk '{while(/^\.* / && sub(/ /,"."));}1' file
top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

这是perl中的一个:

$ perl -p -e 'while(s/(^\.*) /\1./){;}' file
top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

答案 4 :(得分:1)

虽然有点冗长,但却是一种有趣的运动:

with open('file.txt', 'r') as f:
    data = []
    for i, line in enumerate(f):
        # Don't do anything to the field names
        if i == 0:
            new_line = line.rstrip()
        else:
            n_leading_spaces = count_leading_spaces(line)
            # Impute periods for spaces
            new_line = ('.'*n_leading_spaces + line.lstrip()).rstrip()
        data.append(new_line)

最后,打开文件并做一些工作:

>>> print('\n'.join(data))
top           f1    f2    f3
...sub1       f1    f2    f3
...sub2       f1    f2    f3
......sub21   f1    f2    f3
...sub3       f1    f2    f3

结果:

with open('file.txt', 'r') as f:
    data = []
    for i, line in enumerate(f):
        # Don't do anything to the field names
        if i == 0:
            new_line = line.rstrip()
        else:
            n_leading_spaces = len(line) - len(line.lstrip())
            # Impute periods for spaces
            new_line = line.lstrip().rjust(len(line), '.').rstrip()
        data.append(new_line)

你也可以这样做,这更简单:

.form-horizontal