การใช้งาน regexp แบบขยายเพื่อเพิ่มจำนวนตัวแปรของศูนย์นำหน้าตามตำแหน่งในสตริง


10

ฉันมีปัญหาในการทำให้ซิงก์ของฉันช้าลงเพื่อเพิ่มตัวเลขนำหน้าจำนวนหนึ่งให้กับรูปแบบองค์กรตัวเลข สตริงที่ฉันกำลังทำงานอยู่นั้นดูเหมือนว่า

1.1.1.1,Some Text Here

ใช้ประโยชน์จากไวยากรณ์ซิด

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

ฉันสามารถกระตุ้นการตอบสนองได้

01.01.01.01,Some Text Here

อย่างไรก็ตามสิ่งที่ฉันกำลังมองหาคือสิ่งที่เติมศูนย์ถึง 2 หลักในฟิลด์ 2 และ 3 และ 3 และ 3 หลักในฟิลด์ 4 เพื่อให้รายการทั้งหมดมีความยาวมาตรฐานที่ [0-9] [0-9] { 2}. [0-9] {2}. [0-9] {3}

1.01.01.001,Some Text Here

สำหรับชีวิตของฉันฉันไม่สามารถคิดได้ถึงวิธีการปรับเปลี่ยนขอบเขตเพื่อรวมพารามิเตอร์ที่จำเป็นในการถ่ายภาพเป็นตัวเลขเท่านั้นหลังจากระยะ ฉันคิดว่ามันมีบางอย่างเกี่ยวกับการใช้ \ b ซึ่งฉันเข้าใจการจับคู่ศูนย์อักขระที่ขอบเขตคำ แต่ฉันไม่เข้าใจว่าทำไมความพยายามของฉันในการเพิ่มช่วงเวลาในการแข่งขันล้มเหลวดังนี้:

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

นอกจากนี้ฉันคาดหวังว่าฉันจะมีปัญหาเพิ่มเติมหากคำสั่งมีข้อความเช่น:

1.1.1.1,Some Number 1 Here

มันเป็นข้อสรุปมาก่อนว่าฉันจำเป็นต้องเรียนรู้ที่แท้จริงและความซับซ้อนทั้งหมดของมัน ฉันกำลังดำเนินการอยู่ แต่คาดว่าข้อความนี้จะทำให้ฉันเดือดร้อนอยู่พักหนึ่ง ความช่วยเหลือใด ๆ ที่จะได้รับการชื่นชมอย่างมาก.

แก้ไข: ฉันพบวิธี ... ข้อความนี้ดูเหมือนจะทำสิ่งที่ฉันกำลังมองหา แต่ต้องมีวิธีที่สง่างามกว่านี้

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

นอกจากนี้วากยสัมพันธ์นี้จะทำให้เกิดปัญหาหากรูปแบบตัวเลขที่คล้ายกันปรากฏในข้อความ ... คล้ายกับ:

1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3

ในกรณีนี้มันจะส่งผลให้:

1.01.01.001,Some Text Referring to Document XXX Heading 01.02.03

แก้ไขแล้ว ขอบคุณสำหรับความช่วยเหลือของคุณที่นี่ ฉันเริ่มแก้ไขปัญหาด้วยคำตอบที่ฉันตอบไว้ด้านล่าง ฉันรู้สึกว่าได้ย้ายโซลูชันไปยัง Python ซึ่งเป็นส่วนหนึ่งของโซลูชันที่มีขนาดใหญ่กว่าซึ่งใช้ประโยชน์จากการจัดเรียงด้านล่าง:

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

s=sorted(reader, key=getPaddedKey)

ดูเหมือนว่าจะทำสิ่งที่ฉันกำลังมองหา: sed -r ':r;s/\b[0-9]{1,1}\.\b/0&/;tr;:i;s/\b[0-9]{1,2},\b/0&/;ti;s/.//' อย่างไรก็ตามฉันชอบที่จะรู้ว่ามีวิธีการที่สง่างามมากขึ้น
daijizai

1
น่าประหลาดที่การย้อนกลับสตริงใช้ศูนย์ต่อท้ายแล้วการย้อนกลับผลลัพธ์อาจบรรลุเป้าหมายของคุณได้ง่ายขึ้น
roaima

2
การใช้printf(หรือprintfโทรภายใน Awk) อาจตรงไปตรงมามากกว่า
Wildcard

1
นี่เป็นสิ่งที่จะนำไปใช้อ่านเข้าใจและแก้ไขได้ง่ายขึ้นในอนาคตในภาษาเช่น awk หรือ perl (หรือสิ่งอื่นใดที่มี printf และการแยกฟิลด์อย่างง่าย)
cas

1
@ Wildcard - จุดที่ดี คุณช่วยชี้ให้ฉันดูบางอย่างเกี่ยวกับการแก้ไขข้อบกพร่องได้หรือไม่ โดยปกติแล้วฉันใช้วิธีการจ้องมองเป็นเวลานานโดยใช้คำสบถ ;) ยกเว้นว่าบางครั้งฉันแบ่งคำสั่ง sed เป็นชิ้นเล็ก ๆ และพยายามให้แต่ละอันทำงานก่อนที่จะรวมเข้าด้วยกันอีกครั้ง ฉันเพิ่งอ่านบทช่วยสอนที่ยอดเยี่ยมgithub.com/learnbyexample/Command-line-text-processing/blob/ ......และฉันแน่ใจว่าตัวอย่างบางส่วนผิดไปจนกระทั่งฉันใช้การจ้องมองเป็นเวลานาน
Joe

คำตอบ:


4

การใช้งาน: 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"

คำอธิบาย:

  1. การลบครั้งแรกเพิ่มจำนวนศูนย์ให้กับแต่ละตัวเลข 1 ศูนย์ถึง 2 และ 3 หมายเลข 2 ศูนย์ถึง 4 หมายเลข ไม่สำคัญหรอกว่ามีกี่หลักอยู่แล้ว
  2. substution ที่สองจะลบศูนย์พิเศษทั้งหมดออกจากจำนวนที่ต้องการเท่านั้น ตัวเลข 2 และ 3 ควรมีตัวเลข 2 หลักเท่านั้น ออกจากพวกเขาและลบที่วางอยู่ หมายเลขที่สี่ควรมีเพียง 3 หลัก ออกจากพวกเขาและลบที่วางอยู่

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

ในขณะที่ในที่สุดฉันเพิ่งจบลงสคริปต์นี้ใน Python เพื่อความได้เปรียบนี่เป็นคำตอบที่ดีที่สุดสำหรับคำถามของฉันตามที่เขียนไว้ว่า perl ส่งแบ็กสแลชที่ลบออกก่อนหน้านี้ (อย่างน้อย) จากเอาท์พุท 1. นี่คือวิธีการแก้ปัญหาและ 2. ผลิตผลลัพธ์ที่เหมาะสมโดยไม่ต้องทำร้ายข้อความ ทำเครื่องหมายว่าเป็นคำตอบ ขอบคุณ! :-)
daijizai

@daijizai ตามที่ฉันได้แสดงให้เห็นแล้วperlรุ่นไม่ลบแบ็กสแลช
roaima

9

bash สามารถจัดการสิ่งนี้ได้ มันจะช้ากว่า 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
หรือ Awk แต่ +1 สำหรับการใช้printfเครื่องมือที่เหมาะสม (Awk มีprintfและได้รับการออกแบบที่ดีกว่าbashการประมวลผลข้อความด้วย) ดูที่เหตุใดการใช้เชลล์ลูปเพื่อประมวลผลข้อความจึงถือว่าไม่เหมาะสม
Wildcard

5

คุณยังไม่ได้ขอperlวิธีแก้ปัญหาเป็นพิเศษแต่นี่คือวิธีแก้ปัญหา โดยส่วนตัวแล้วฉันคิดว่ามันอ่านง่ายกว่านิดหน่อยโดยเฉพาะเมื่อแบ่งออกเป็นหลายบรรทัด

ครั้งแรกที่นี่คือหนึ่งซับ:

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

ผลของมัน:

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

และนี่คือperlสคริปต์ที่แตกออกและแสดงความคิดเห็น ( -nธงทำให้while read; do ... doneวนรอบโดยนัยรอบโค้ด):

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

กระแทกแดกดันฉันเพิ่งจะยอมแพ้และย้ายไป awk เมื่อคุณโพสต์นี้ ดูเหมือนว่าจะพอดีกับใบเสร็จ ฉันจะตรวจสอบและกลับมา
daijizai

@daijizai awkก็จะทำงานเหมือนกัน - ใช้หลักการเดียวกันprintf
roaima

สิ่งเดียวที่สิ่งนี้ล้มเหลวที่ฉันคาดไม่ถึง แต่มีความสำคัญ ดูเหมือนว่าจะลบเครื่องหมายแบ็กสแลชจากส่วนข้อความ
daijizai

@daijizai ไม่ได้อยู่ที่นี่มันไม่ได้ คุณป้อนข้อความด้วยแบ็กสแลชเป็นอย่างไร? ฉันได้เพิ่มตัวอย่างแบ็กสแลชสำหรับคุณแล้ว
roaima

ในการใช้กับชุดข้อมูลภายในของฉันมีแถวที่มีคอลัมน์ข้อความที่มีสตริงเช่น SOME \ Text \ Might \ Be \ Here \ 4Realz เมื่อชุดข้อมูลนี้ถูกส่งผ่านไปยังคำสั่ง perl มันทำให้เกิดการตอบสนองเช่น SOMETextMightBeHere4Realz
daijizai

3

นี่เป็นวิธีหนึ่งที่เป็นไปได้:
sed -E 's/([0-9]*\.)/0\1/g;s/.//;s/([0-9]*,)/00\1/'

ตัวอย่าง

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

ทำงานกับสตริงนี้ด้วย:

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

... และสตริงนี้:

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

โชคไม่ดีที่สิ่งนี้พังทลายลงเมื่อตัวเลขปีนขึ้นไป ตัวอย่างเช่น: 1.1.11.111, ข้อความบางส่วนที่นี่กลายเป็น: 1.1.101.11001, ข้อความบางส่วนที่นี่
daijizai

@daijizai โปรดดูการแก้ไขของฉัน สิ่งนี้จะตอบสนองความต้องการหรือไม่
maulinglawns

น่าเสียดายที่ไม่ใช่ แต่ฉันคิดว่านั่นอาจเป็นความผิดของฉัน ความต้องการในการเติมศูนย์เป็นสองหลักในฟิลด์ 2 และ 3 และ 3 ในฟิลด์ 4 โดยพื้นฐาน [0-9]. [0-9] [0-9] {2}. [0-9] {2}. [0] -9] {3}, ข้อความบางส่วนที่นี่
daijizai

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

คำอธิบาย:

วิธีที่ใช้คือที่นี่คือการดูละแวกใกล้เคียงของตัวเลขและดำเนินการตามนั้น ดังนั้นตัวเลขที่ 2 และ 3 จะเห็นจุดทั้งสองข้างในขณะที่ตัวเลขที่ 4 เห็นจุดที่อยู่ทางซ้ายและเครื่องหมายจุลภาคอยู่ด้านขวา

$ 1 ถูกตั้งค่าเมื่อ regex ใช้เส้นทางของจำนวนที่ 2 หรือที่ 3 และตามด้วยการเติมที่แม่นยำคือ 2 OTOH สำหรับ num ที่ 4 การ padding คือ 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

ผล:

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.