Menerapkan regexp yang diperluas untuk menambahkan sejumlah variabel nol terkemuka berdasarkan posisi dalam sebuah string


10

Saya mengalami kesulitan menurunkan sintaks sed saya untuk menambahkan angka nol terkemuka yang bervariasi ke skema organisasi numerik. String yang saya operasikan muncul seperti

1.1.1.1,Some Text Here

meningkatkan sintaks sed

sed -r ":r;s/\b[0-9]{1,$((1))}\b/0&/g;tr"

Saya dapat memperoleh respons

01.01.01.01,Some Text Here

Namun, yang saya cari adalah mengisi nol hingga 2 digit di bidang 2 dan 3 dan 3 di bidang 4 sehingga semua item memiliki panjang standar di [0-9]. [0-9] { 2}. [0-9] {2}. [0-9] {3}

1.01.01.001,Some Text Here

Untuk kehidupan saya, saya bahkan tidak bisa membayangkan bagaimana mengubah batas untuk memasukkan parameter yang diperlukan untuk mengambil hanya angka setelah periode. Saya pikir itu ada hubungannya dengan penggunaan \ b yang saya pahami cocok dengan nol karakter pada batas kata, tetapi saya tidak mengerti mengapa upaya saya untuk menambahkan periode pada pertandingan gagal sebagai berikut:

sed -r ":r;s/\.\b[0-9]{1,$((1))}\b/0&/g;tr"
sed -r ":r;s/\b\.[0-9]{1,$((1))}\b/0&/g;tr"
Both cause the statement to hang

sed -r ":r;s/\b[0-9]\.{1,$((1))}\b/0&/g;tr"
sed -r ":r;s/\b[0-9]{1,$((1))}\.\b/0&/g;tr"
sed -r ":r;s/\b[0-9]{1,$((1))}\b\./0&/g;tr"
cause the statement to output:

1.01.01.1,Some Text Here

Selain itu, saya berharap bahwa saya akan memiliki masalah tambahan jika pernyataan itu berisi teks seperti:

1.1.1.1,Some Number 1 Here

Ini adalah kesimpulan terdahulu bahwa saya harus benar-benar belajar sed dan semua kerumitannya. Saya sedang mengerjakan itu, tetapi berharap bahwa pernyataan khusus ini akan terus membuat saya kesulitan untuk sementara waktu. Bantuan apa pun akan sangat dihargai.

EDIT: Saya sudah menemukan cara ... Pernyataan ini tampaknya melakukan apa yang saya cari, tetapi harus ada cara yang lebih elegan untuk melakukan ini.

sed -r ':r;s/\b[0-9]{1,1}\.\b/0&/;tr;:i;s/\b[0-9]{1,2},\b/0&/;ti;s/.//'

Juga, secara sintaksis ini akan menyebabkan masalah jika format angka yang serupa muncul dalam teks ... mirip dengan:

1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3

Dalam hal ini akan menghasilkan:

1.01.01.001,Some Text Referring to Document XXX Heading 01.02.03

Diselesaikan Terima kasih semua atas bantuan Anda di sini. Saya awalnya memecahkan masalah dengan jawaban yang saya terima di bawah. Saya rasa telah memindahkan solusi ke Python sebagai bagian dari solusi yang lebih besar memanfaatkan jenis di bawah ini:

def getPaddedKey(line):
    keyparts = line[0].split(".")
    keyparts = map(lambda x: x.rjust(5, '0'), keyparts)
    return '.'.join(keyparts)

s=sorted(reader, key=getPaddedKey)

Ini tampaknya melakukan apa yang saya cari: sed -r ':r;s/\b[0-9]{1,1}\.\b/0&/;tr;:i;s/\b[0-9]{1,2},\b/0&/;ti;s/.//' Namun, saya ingin tahu apakah ada pendekatan yang lebih elegan.
daijizai

1
Anehnya, membalikkan string, menerapkan nol di belakang , dan kemudian membalikkan hasilnya dapat mencapai tujuan Anda dengan lebih mudah.
roaima

2
Menggunakan printf(atau printfpanggilan dalam Bahasa Inggris) mungkin lebih mudah.
Wildcard

1
ini jelas sesuatu yang akan lebih mudah untuk diimplementasikan, dibaca, dipahami, dan dimodifikasi di masa depan dalam bahasa seperti awk atau perl (atau apa pun yang memiliki printf dan pemecah-bidang yang mudah).
Kasus

1
@ Kartu Memori - poin diambil dengan baik. Bisakah Anda mengarahkan saya ke sesuatu tentang sed debug? Biasanya, saya memilih untuk terus menatap diselingi oleh kata-kata kasar. ;) Kecuali itu, saya terkadang memecah pernyataan sed menjadi bagian-bagian yang lebih kecil dan mencoba membuat masing-masing bekerja sebelum menggabungkannya lagi. Saya baru-baru ini membaca tutorial yang bagus github.com/learnbyexample/Command-line-text-processing/blob/… dan saya yakin beberapa contohnya salah sampai saya menerapkan tatapan yang berkepanjangan.
Joe

Jawaban:


4

Pemakaian: leading_zero.sh input.txt

#!/bin/bash

sed -r '
    s/\.([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,3},)/.0\1.0\2.00\3/
    s/\.0*([0-9]{2})\.0*([0-9]{2})\.0*([0-9]{3})/.\1.\2.\3/
' "$1"

Penjelasan:

  1. Substitusi pertama menambahkan jumlah nol tertentu ke setiap angka. 1 nol hingga 2 dan 3 angka, 2 nol hingga 4 angka. Tidak masalah, berapa digit yang sudah ada.
  2. Substitusi kedua menghapus semua nol tambahan, hanya menyisakan jumlah angka yang diperlukan. 2 dan 3 angka harus mengandung hanya 2 digit. Meninggalkan mereka dan menghapus sisanya. Angka keempat seharusnya hanya mengandung 3 digit. Meninggalkan mereka dan menghapus sisanya.

input.txt

1.1.1.1,Some Text Here
1.1.1.1,Some Text Here
1.11.1.11,Some Text Referring to Document XXX Heading 1.2.3
1.1.1.1,Some Text Here
1.1.11.111,Some Text Referring to Document XXX Heading 1.2.3
1.11.1.1,Some Text Here

output.txt

1.01.01.001,Some Text Here
1.01.01.001,Some Text Here
1.11.01.011,Some Text Referring to Document XXX Heading 1.2.3
1.01.01.001,Some Text Here
1.01.11.111,Some Text Referring to Document XXX Heading 1.2.3
1.11.01.001,Some Text Here

Sementara pada akhirnya saya hanya berakhir menulis ini dengan Python untuk kelayakan, ini adalah jawaban terbaik untuk pertanyaan saya sebagaimana tertulis mengingat bahwa perl yang sebelumnya dikirim dihapus backslash (setidaknya) dari output. 1. ini adalah solusi sed, dan 2. menghasilkan output yang tepat tanpa penganiayaan teks. Menandai sebagai jawaban. Terima kasih! :-)
daijizai

@daijizai seperti yang telah saya tunjukkan, perlversi ini tidak menghapus garis miring terbalik.
roaima

9

bash dapat menangani ini. Ini akan jauh lebih lambat daripada perl:

echo "1.1.1.1,Some Text Here" | 
while IFS=., read -r a b c d text; do
    printf "%d.%02d.%02d.%03d,%s\n" "$a" "$b" "$c" "$d" "$text"
done
1.01.01.001,Some Text Here

2
Atau Awk. Tapi +1 untuk digunakan printf, alat yang masuk akal. (Awk printfjuga dirancang dan lebih baik daripada bashuntuk pemrosesan teks.) Juga lihat Mengapa menggunakan shell loop untuk memproses teks dianggap praktik buruk?
Wildcard

5

Anda belum secara khusus meminta perlsolusi tetapi ini ada satu. Secara pribadi saya pikir ini sedikit lebih mudah dibaca, terutama ketika dipecah menjadi beberapa baris.

Pertama di sini adalah one-liner:

(
    echo '1.2.3.4,Some Text Here'
    echo '1.01.01.1,Some Text Here'
    echo '1.1.1.1,Some Number 1 Here'
    echo '1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3'
    echo '1.2.3.4,Some \n \s \text'
) |
perl -ne '($ip, $text) = split(/,/, $_, 2); $ip = sprintf("%1d.%02d.%03d.%03d", split(/\./, $ip)); print "$ip,$text"'

Hasilnya:

1.02.003.004,Some Text Here
1.01.001.001,Some Text Here
1.01.001.001,Some Number 1 Here
1.01.001.001,Some Text Referring to Document XXX Heading 1.2.3
1.02.003.004,Some \n \s \text

Dan inilah perlskrip yang rusak dan dikomentari ( -nflag menempatkan while read; do ... doneloop implisit di sekitar kode):

($ip, $text) = split(/,/, $_, 2);                # Split line into two parts by comma
@octets = split(/\./, $ip)                       # Split IP address into octets by dots
$ip = sprintf("%1d.%02d.%03d.%03d", @octets);    # Apply the formatting
print "$ip,$text"                                # Output the two parts

Ironisnya, saya baru saja akan menyerah dan pindah ke awk ketika Anda memposting ini. Tampaknya sesuai dengan tagihan. Saya akan memeriksanya dan kembali.
daijizai

@daijizai awkakan bekerja juga - prinsip yang sama menggunakanprintf
roaima

Satu-satunya hal yang gagal pada saya tidak bisa diantisipasi, tetapi signifikan. Tampaknya menghapus garis miring terbalik dari bagian teks.
daijizai

@daijizai tidak di sini tidak. Bagaimana Anda memberi makan teks dengan garis miring terbalik? Saya telah menambahkan contoh backslashed untuk Anda
roaima

Dalam penggunaan saya dengan dataset internal saya ada baris dengan kolom teks yang berisi string seperti BEBERAPA \ Text \ Might \ Be \ Here \ 4Realz. Ketika dataset ini diteruskan ke pernyataan perl, ini menghasilkan respons seperti SOMETextMightBeHere4Realz
daijizai

3

Inilah satu pendekatan yang mungkin:
sed -E 's/([0-9]*\.)/0\1/g;s/.//;s/([0-9]*,)/00\1/'

Contohnya

echo "1.11.111.1111,Some Text Here" | sed -E 's/([0-9]*\.)/0\1/g;s/.//;s/([0-9]*,)/00\1/'
1.011.0111.001111,Some Text Here

Juga bekerja dengan string ini:

echo "1.1.1.1,Some Number 1 Here" | sed -E 's/([0-9]\.)/0\1/g;s/.//;s/([0-9],)/00\1/'
1.01.01.001,Some Number 1 Here

... dan string ini:

echo "1.2.2101.7191,Some Text Here" | sed -E 's/([0-9]*\.)/0\1/g;s/.//;s/([0-9]*,)/00\1/'
1.02.02101.007191,Some Text Here

Sayangnya ini rusak saat angka naik. Sebagai contoh: 1.1.11.111, Some Text Here Became: 1.1.101.11001, Some Text Here
daijizai

@daijizai Silakan lihat edit saya. Apakah ini memenuhi persyaratan?
maulinglawns

Sayangnya tidak, tapi saya pikir itu mungkin salah saya. Kebutuhan nol-isi terdiri dari dua digit pada bidang 2 dan 3 dan 3 digit pada bidang 4. Pada dasarnya [0-9]. [0-9] {2}. [0-9] {2}. [0 -9] {3}, Some Text Here
daijizai

2
perl -pe '/^\d/g && s/\G(?:(\.\K\d+(?=\.))|\.\K\d+(?=,))/sprintf "%0".($1?2:3)."d",$&/ge'

Penjelasan:

Metode yang digunakan di sini adalah dengan melihat lingkungan numerik dan mengambil tindakan berdasarkan itu. Jadi, angka 2 dan 3 melihat titik di kedua sisi sedangkan angka 4 melihat titik di sebelah kiri dan koma di sebelah kanan.

$ 1 diatur ketika regex mengambil lintasan ke-2 atau ke-3 dan sesuai dengan ketelitian bantalan 2. OTOH, untuk angka ke-4, bantalan adalah 3.

% cat file.txt

1.00.3.4,Some Text Here
1.01.01.1,Some Text Here
1.0.01.1,Some Number 1 Here
1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3.4
1.2.3.4,Some \n \s \text

Hasil:

1.00.03.004,Some Text Here
1.01.01.001,Some Text Here
1.00.01.001,Some Number 1 Here
1.01.01.001,Some Text Referring to Document XXX Heading 1.2.3.4
1.02.03.004,Some \n \s \text
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.