Triển khai một biểu thức chính mở rộng để thêm số lượng các số 0 đứng đầu dựa trên vị trí trong chuỗi


10

Tôi gặp khó khăn khi đưa cú pháp sed của mình xuống để thêm một số lượng khác nhau dẫn đầu vào sơ đồ tổ chức số. Các chuỗi tôi đang hoạt động xuất hiện như

1.1.1.1,Some Text Here

tận dụng cú pháp sed

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

Tôi có thể gợi ra câu trả lời

01.01.01.01,Some Text Here

Tuy nhiên, điều tôi đang tìm kiếm là thứ gì đó bằng không - điền tối đa 2 chữ số vào các trường 2 và 3 và 3 chữ số trong trường 4 để tất cả các mục có độ dài tiêu chuẩn ở [0-9]. [0-9] { 2}. [0-9] {2}. [0-9] {3}

1.01.01.001,Some Text Here

Đối với cuộc sống của tôi, tôi không thể tìm ra cách sửa đổi ranh giới để bao gồm các tham số cần thiết để chỉ chuyển sang các chữ số sau một khoảng thời gian. Tôi nghĩ rằng nó có liên quan đến việc sử dụng \ b mà tôi hiểu khớp với các ký tự 0 ở ranh giới từ, nhưng tôi không hiểu tại sao những nỗ lực của tôi để thêm một khoảng thời gian vào trận đấu lại thất bại như sau:

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

Ngoài ra, tôi hy vọng rằng tôi sẽ có thêm vấn đề nếu câu lệnh chứa văn bản như:

1.1.1.1,Some Number 1 Here

Đó là một kết luận bỏ qua mà tôi cần phải thực sự học về sed và tất cả sự phức tạp của nó. Tôi đang làm việc trên đó, nhưng hy vọng rằng tuyên bố cụ thể này sẽ tiếp tục gây rắc rối cho tôi trong một thời gian. Mọi sự trợ giúp sẽ rất được trân trọng.

EDIT: Tôi đã tìm ra một cách ... Tuyên bố này dường như làm những gì tôi đang tìm kiếm, nhưng phải có một cách thanh lịch hơn để làm điều này.

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

Ngoài ra, về mặt cú pháp, điều này sẽ gây ra vấn đề nếu một định dạng số tương tự xuất hiện trong văn bản ... tương tự như:

1.1.1.1,Some Text Referring to Document XXX Heading 1.2.3

Trong trường hợp đó, nó sẽ dẫn đến:

1.01.01.001,Some Text Referring to Document XXX Heading 01.02.03

Đã giải quyết Cảm ơn tất cả sự giúp đỡ của bạn ở đây. Ban đầu tôi đã giải quyết vấn đề bằng câu trả lời tôi chấp nhận dưới đây. Tôi cảm thấy đã chuyển giải pháp sang Python như một phần của giải pháp lớn hơn tận dụng các loại dưới đây:

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

s=sorted(reader, key=getPaddedKey)

Điều này dường như làm những gì tôi đang tìm kiếm: sed -r ':r;s/\b[0-9]{1,1}\.\b/0&/;tr;:i;s/\b[0-9]{1,2},\b/0&/;ti;s/.//' Tuy nhiên, tôi muốn biết liệu có một cách tiếp cận thanh lịch hơn.
daijizai

1
Thật kỳ lạ, đảo ngược chuỗi, áp dụng các số 0 ở cuối , và sau đó đảo ngược kết quả có thể đạt được mục tiêu của bạn dễ dàng hơn.
roaima

2
Sử dụng printf(hoặc một printfcuộc gọi trong Awk) có thể đơn giản hơn.
tự đại diện

1
đây chắc chắn là thứ sẽ dễ thực hiện, đọc, hiểu và sửa đổi trong tương lai bằng ngôn ngữ như awk hoặc perl (hoặc bất cứ thứ gì khác có printf và tách trường dễ dàng).
cas

1
@Wildcard - điểm cũng được thực hiện. Bạn có thể chỉ cho tôi một cái gì đó về gỡ lỗi sed? Thông thường, tôi dùng đến việc nhìn chằm chằm kéo dài bởi những lời quảng cáo. ;) Chặn rằng, đôi khi tôi chia một câu lệnh sed thành các phần nhỏ hơn và cố gắng làm cho mỗi cái hoạt động trước khi kết hợp chúng lại. Gần đây tôi đã đọc một hướng dẫn tuyệt vời github.com/learnbyexample/Command-line-text- Processing / blob / ám và tôi chắc chắn một số ví dụ đã sai cho đến khi tôi áp dụng việc nhìn chằm chằm kéo dài.
Joe

Câu trả lời:


4

Sử dụng: 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"

Giải trình:

  1. Phụ đề đầu tiên thêm số lượng không nhất định vào mỗi số. 1 số 0 đến 2 và 3 số, 2 số 0 đến 4 số. Không quan trọng, có bao nhiêu chữ số.
  2. Sự thay thế thứ hai loại bỏ tất cả các số không thừa, chỉ để lại số lượng cần thiết. 2 và 3 số chỉ được chứa 2 chữ số. Lá chúng và loại bỏ phần còn lại. Số thứ tư chỉ nên chứa 3 chữ số. Lá chúng và loại bỏ phần còn lại.

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

Mặc dù cuối cùng tôi đã kết thúc việc viết kịch bản này bằng Python vì sự nhanh chóng, đây là câu trả lời tốt nhất cho câu hỏi của tôi khi được viết rằng perl đã gửi trước đó đã xóa các dấu gạch chéo ngược (ít nhất) từ đầu ra. Đây là một giải pháp sed và 2. tạo ra đầu ra thích hợp mà không bị lột xác của văn bản. Đánh dấu là câu trả lời. Cảm ơn! :-)
daijizai

@daijizai như tôi đã chứng minh, perlphiên bản không xóa dấu gạch chéo ngược.
roaima

9

bash có thể xử lý này. Nó sẽ chậm hơn rất nhiều so với 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
Hoặc Awk. Nhưng +1 để sử dụng printf, công cụ hợp lý. (Awk printfcũng có và được thiết kế tốt hơn bashđể xử lý văn bản.) Cũng xem Tại sao sử dụng vòng lặp shell để xử lý văn bản được coi là thực tiễn xấu?
tự đại diện

5

Bạn chưa yêu cầu một perlgiải pháp cụ thể nhưng dù sao đây cũng là một giải pháp. Cá nhân tôi nghĩ rằng điều này dễ đọc hơn một chút, đặc biệt là khi chia thành nhiều dòng.

Đầu tiên ở đây là một lớp lót:

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

Kết quả của nó:

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

Và đây là đoạn perlscript được chia ra và nhận xét ( -ncờ đặt một while read; do ... donevòng lặp ngầm xung quanh mã):

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

Trớ trêu thay, tôi vừa định từ bỏ sed và chuyển sang awk khi bạn đăng bài này. Nó có vẻ phù hợp với các hóa đơn. Tôi sẽ kiểm tra nó và lấy lại.
daijizai

@daijizai awkcũng sẽ hoạt động - cùng một nguyên tắc sử dụngprintf
roaima

Điều duy nhất thất bại ở tôi không thể lường trước được, nhưng rất quan trọng. Nó dường như loại bỏ dấu gạch chéo ngược từ phần văn bản.
daijizai

@daijizai không có ở đây. Làm thế nào bạn cho nó ăn văn bản với một dấu gạch chéo ngược? Tôi đã thêm một ví dụ gạch chéo cho bạn
roaima

Trong sử dụng với tập dữ liệu nội bộ của tôi, có các hàng với cột văn bản chứa các chuỗi như SOME \ Text \ Might \ Be \ Here \ 4Realz. Khi tập dữ liệu này được chuyển đến câu lệnh perl, nó đã dẫn đến một phản hồi như SOMETextMightBeHere4Realz
daijizai

3

Đây là một cách tiếp cận có thể:
sed -E 's/([0-9]*\.)/0\1/g;s/.//;s/([0-9]*,)/00\1/'

Ví dụ

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

Cũng làm việc với chuỗi này:

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

... và chuỗi này:

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

Thật không may, điều này bị phá vỡ khi các con số leo lên. Ví dụ: 1.1.11.111, Một số văn bản ở đây đã trở thành: 1.1.101.11001, Một số văn bản ở đây
daijizai 18/07/17

@daijizai Xin vui lòng xem chỉnh sửa của tôi. Điều này sẽ đáp ứng yêu cầu?
malawlawns 18/07/17

Đáng tiếc là không, nhưng tôi nghĩ đó có thể là lỗi của tôi. Nhu cầu điền không có hai chữ số trên trường 2 và 3 và 3 chữ số trên trường 4. Về cơ bản [0-9]. [0-9] {2}. [0-9] {2}. [0 -9] {3}, Một số văn bản ở đây
daijizai

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

Giải trình:

Phương pháp được sử dụng ở đây là xem xét các vùng lân cận của số và thực hiện hành động dựa trên đó. Vì vậy, số thứ 2 và thứ 3 nhìn thấy một dấu chấm ở cả hai bên trong khi số thứ 4 nhìn thấy dấu chấm ở bên trái và dấu phẩy ở bên phải.

$ 1 được đặt khi regex lấy đường dẫn của chữ số thứ 2 hoặc thứ 3 và theo đó, phần đệm chính xác là 2. OTOH, đối với chữ số thứ 4, phần đệm là 3.

% tập tin mèo

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

Các kết quả:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.