Bir dizedeki konuma bağlı olarak, değişken sayıda önde gelen sıfır eklemek için uzatılmış bir normal ifadeyi uygulama


10

Sayısal organizasyon şemasına değişken sıfırlar eklemek için sed sözdizimimi almayla ilgili sorun yaşıyorum. Üzerinde çalıştığım dizeler

1.1.1.1,Some Text Here

sed sözdiziminden yararlanma

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

Cevabı ortaya çıkarabiliyorum

01.01.01.01,Some Text Here

Ancak, aradığım şey, alan 4 ve 2 ve 3 ve 3 basamaklı alanlardaki 2 basamağa kadar sıfır dolduracak bir şeydir, böylece tüm öğeler [0-9] 'da standart uzunluktadır. [0-9] { 2}. [0-9] {2}. [0-9] {3}

1.01.01.001,Some Text Here

Hayatım boyunca, sadece bir periyodu takip eden rakamlara yapışmak için gerekli parametreleri içerecek şekilde sınırın nasıl değiştirileceğini bile anlayamıyorum. Ben bir kelime sınırında sıfır karakter eşleştiğini anlamak \ b kullanımı ile ilgili bir şey olduğunu düşünüyorum, ama neden maç için bir dönem eklemek için denemelerimi aşağıdaki gibi başarısız anlamıyorum:

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

Ayrıca, ifade gibi metin içeriyorsa ek sorunlar olacağını bekliyoruz:

1.1.1.1,Some Number 1 Here

Sed ve tüm karmaşıklıklarını gerçekten öğrenmem gereken, vazgeçilmez bir sonuçtur. Bunun üzerinde çalışıyorum, ancak bu ifadenin bir süre bana sorun çıkarmaya devam edeceğini umuyorum. Herhangi bir yardım büyük mutluluk duyacağız.

DÜZENLEME: Bir yol buldum ... Bu ifade aradığım şeyi yapıyor gibi görünüyor, ama bunu yapmak için daha zarif bir yolu olmalı.

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

Ayrıca, sözdizimsel olarak metinde benzer bir sayı biçimi görünürse sorunlara neden olur ... benzer:

1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3

Bu durumda aşağıdakilerle sonuçlanır:

1.01.01.001,Some Text Referring to Document XXX Heading 01.02.03

Çözüldü Yardımlarınız için hepinize teşekkür ederim. Sorunu başlangıçta aşağıda kabul ettiğim cevapla çözdüm. Aşağıdaki tür kaldıraç daha büyük bir çözümün bir parçası olarak çözüm Python taşındı hissettim:

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

s=sorted(reader, key=getPaddedKey)

Bu aradığım şeyi yapıyor gibi görünüyor: sed -r ':r;s/\b[0-9]{1,1}\.\b/0&/;tr;:i;s/\b[0-9]{1,2},\b/0&/;ti;s/.//' Ancak, daha zarif bir yaklaşım olup olmadığını bilmek isterim.
daijizai

1
Tuhaf bir şekilde, ipi tersine çevirmek, sondaki sıfırları uygulamak ve ardından sonucu tersine çevirmek hedefinize daha kolay ulaşabilir.
roaima

2
Kullanılması printf(veya printfawk içinde arama) daha kolay olabilir.
Wildcard

1
bu kesinlikle awk veya perl gibi bir dilde (veya printf ve kolay alan bölme özelliğine sahip bir dilde) uygulanması, okunması, anlaşılması ve değiştirilmesi daha kolay olacak bir şeydir.
cas

1
@Wildcard - iyi alınan nokta. Beni sed hata ayıklama hakkında bir şeye işaret edebilir misiniz? Genellikle, küfürler tarafından noktalanmış uzun süreli bakıma başvururum. ;) Buna engel olarak, bazen bir sed ifadesini daha küçük parçalara bölerim ve her birini tekrar birleştirmeden önce işe koymaya çalışırım. Son zamanlarda harika bir öğretici github.com/learnbyexample/Command-line-text-processing/blob/… okudum ve uzun süreli bakışı uygulayana kadar bazı örneklerin yanlış olduğundan emindim.
Joe

Yanıtlar:


4

Kullanımı: 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"

Açıklama:

  1. İlk altyazı, her sayıya belirli miktarda sıfır ekler. 1 sıfır ila 2 ve 3 sayı, 2 sıfır ila 4 sayı. Önemli değil, zaten ne kadar basamak var.
  2. İkinci ayırma, tüm gerekli sıfırları kaldırır ve yalnızca gereken sayıda sayı bırakır. 2 ve 3 sayıları yalnızca 2 basamak içermelidir. Onları bırakır ve dinlenmeyi giderir. Dördüncü sayı sadece 3 hane içermelidir. Onları bırakır ve dinlenmeyi giderir.

girdi.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

Sonunda ben sadece Python için uygunluk için komut dosyası sona erdi iken, daha önce gönderilen perl çıktı (en azından) ters eğik çizgiler kaldırıldı göz önüne alındığında, bu yazılı olarak benim soruya en iyi cevap budur. Bu 1. sed çözümdür ve 2. metnin molestasyonu olmadan uygun çıktıyı üretir. Cevap olarak işaretleme. Teşekkürler! :-)
daijizai

@daijizai zaten gösterdiğim gibi, perlsürüm ters eğik çizgileri kaldırmaz.
roaima

9

bash bunu halledebilir. Yine de perl'den çok daha yavaş olacak:

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
Veya Awk. Ama printfmantıklı araç kullanmak için +1 . (Awk printfayrıca bashmetin işlemeye göre daha iyi tasarlanmış ve tasarlanmıştır .) Ayrıca bkz. Neden kötü uygulama olarak kabul edilen metni işlemek için bir kabuk döngüsü kullanılıyor?
Wildcard

5

Özellikle bir perlçözüm istemediniz, ama yine de bir çözüm. Şahsen, özellikle birkaç satıra bölünmüş olduğunda bunun okunması biraz daha kolay olduğunu düşünüyorum.

İlk olarak tek astar:

(
    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"'

Sonuçları:

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

Ve işte perlkomut dosyası ayrıldı ve yorumlandı ( -nbayrak while read; do ... donekodun etrafına örtülü bir döngü koyar ):

($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

İronik bir şekilde, sed'den vazgeçmek ve bunu yayınladığınızda awk'a geçmek üzereydim. Faturaya uyuyor gibi görünüyor. Kontrol edip geri döneceğim.
daijizai

@daijizai awkde çalışır - aynı prensibi kullanarakprintf
roaima

Bunun başarısız olduğu tek şey tahmin edilemezdi, ama önemli. Metin bölümünden ters eğik çizgi çıkıyor gibi görünüyor.
daijizai

@daijizai burada değil. Metni ters eğik çizgiyle nasıl besliyorsunuz? Senin için ters eğik bir örnek ekledim
roaima

Dahili veri kümemle kullanımımda, metin sütununda SOME \ Text \ Might \ Be \ Here \ 4Realz gibi dizeler içeren satırlar var. Bu veri seti perl deyimine geçtiğinde SOMETextMightBeHere4Realz
daijizai

3

İşte olası bir yaklaşım:
sed -E 's/([0-9]*\.)/0\1/g;s/.//;s/([0-9]*,)/00\1/'

Örnekler

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

Ayrıca bu dizeyle çalışın:

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

... ve şu dize:

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

Ne yazık ki bu rakamlar arttıkça bozulur. Örneğin: 1.1.11.111, Buraya Bazı Metin Oldu: 1.1.101.11001, Buraya Bazı Metin
daijizai

@daijizai Lütfen düzenlememe bakın. Bu gereksinimi karşılar mı?
maulinglawns

Ne yazık ki değil, ama bence bu benim hatam olabilir. Sıfır dolgusunun alan 2'de iki iki basamaklı olması ve alan 4'te 3 basamaklı olması gerekir. Esasen [0-9]. [0-9] {2}. [0-9] {2}. [0 -9] {3}, Bazı Metinler Burada
daijizai

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

Açıklama:

Burada kullanılan yöntem, nümeriklerin mahallelerine bakmak ve buna dayanarak harekete geçmektir. Böylece, 2. ve 3. sayılar her iki tarafta bir nokta görürken, 4. sayısal rakam solda ve sağda bir virgül görür.

Regex, 2. veya 3. sayıların yolunu aldığında 1 $ ayarlanır ve buna göre hassas dolgu 2'dir. OTOH, 4. num için dolgu 3'tür.

% 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

Sonuçlar:

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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.