Pertanyaan Ekspresi reguler - ganti semua spasi di awal baris dengan titik


Saya tidak peduli jika saya mencapai ini melalui vim, sed, awk, python dll. Saya mencoba semuanya, tidak bisa menyelesaikannya.

Untuk masukan seperti ini:

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

Saya ingin:

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

Lalu saya ingin memuat ini di Excel (dibatasi oleh spasi) dan masih dapat melihat hierarki-ness kolom pertama!

Saya mencoba banyak hal, tetapi akhirnya kehilangan informasi hierarki


5
2017-10-03 23:31


asal


Jawaban:


Dengan ini sebagai input:

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

Mencoba:

$ 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

Bagaimana itu bekerja:

  • :a

    Ini menciptakan label a.

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

    Jika garis dimulai dengan spasi, ini menggantikan ruang terakhir di ruang terdepan dengan periode.

    Lebih detail, ^( *)  cocok dengan semua yang kosong kecuali yang terakhir dan menyimpannya dalam grup 1. Regex ([^ ]) (yang, terlepas dari apa yang tampak seperti stackoverflow, terdiri dari kosong diikuti oleh ([^ ])) cocok dengan kosong diikuti oleh yang tidak kosong dan menyimpan kelompok tidak kosong di grup 2.

    \1.\2 mengganti teks yang cocok dengan grup 1, diikuti oleh periode, diikuti oleh grup 2.

  • ta

    Jika perintah substitusi menghasilkan substitusi, kemudian kembali ke label a dan coba lagi.

Kesesuaian:

  1. Di atas diuji pada GNU sed modern. Untuk BSD / OSX sed, orang mungkin atau mungkin tidak perlu menggunakan:

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

    Pada GNU kuno, yang perlu digunakan -r di tempat -E:

    sed -r ':a; s/^( *) ([^ ])/\1.\2/; ta' file
    
  2. Di atas diasumsikan bahwa ruang kosong. Jika mereka tab, maka Anda harus memutuskan apa tabstop Anda dan membuat pergantian sesuai.


4
2017-10-03 23:41



Ada dua cara berbeda untuk melakukan ini dalam vim.

  1. Dengan regex:

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

    Ini cukup mudah, tetapi sedikit verbose. Ini menggunakan daftar eval (\=) untuk menghasilkan string '.'memiliki panjang yang sama dengan jumlah spasi di awal setiap baris.

  2. Dengan perintah norma:

    :%norm ^hviwr.
    

    Ini adalah perintah singkat yang jauh lebih mudah, meskipun sedikit lebih sulit untuk dipahami. Ini secara visual memilih ruang di awal baris, dan menggantikan seluruh pilihan dengan titik-titik. Jika tidak ada ruang utama, perintah akan gagal ^h karena kursor berusaha keluar dari batas.

    Untuk melihat cara kerjanya, coba ketikkan ^hviwr. pada garis yang memiliki ruang terdepan untuk melihat itu terjadi.


5
2017-10-03 23:46



Sejak Anda berkata 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))

(untuk setiap baris, kita mengganti jangka awalan dari spasi awalan '^ +' dengan serangkaian titik yang sama panjangnya, len(m.group(0)) * '.').

Dengan hasil akhir:

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

Sejak Anda berkata 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

(di mana untuk setiap baris kami mencocokkan prefiks terlama spasi dengan match, ekstrak dengan substr, ganti setiap ruang dengan titik via gsub, dan cetak awalan yang dimodifikasi p, diikuti oleh sisa garis masukan (RSTART dan RLENGTH variabel diisi setelah match() dan tahan posisi awal dan panjang pola yang cocok).


3
2017-10-04 00:41



Pada awk. Itu terus mengganti ruang pertama dengan periode sementara ruang hanya didahului oleh periode:

$ 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

dan inilah satu di 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

3
2017-10-04 03:49



Agak panjang, tetapi latihan yang menyenangkan tetap:

# Function to count the number of leading spaces in a string
# Basically, this counts the number of consecutive elements that satisfy being spaces
def count_leading_spaces(s):
    if not s:
        return 0
    else:
        curr_char = s[0]
        if curr_char != ' ':
            return 0
        else:
            idx = 1
            curr_char = s[idx]
            while curr_char == ' ':
                idx += 1
                try:
                    curr_char = s[idx]
                except IndexError:
                    return idx
        return idx

Akhirnya, buka file dan lakukan beberapa pekerjaan:

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)

Hasil:

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

Anda juga bisa melakukannya dengan cara ini, yang jauh lebih mudah:

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)

1
2017-10-04 00:00