Một regex để phân tích cú pháp số phiên bản


84

Tôi có số phiên bản của biểu mẫu sau:

version.release.modification

trong đó phiên bản, bản phát hành và sửa đổi là một tập hợp các chữ số hoặc ký tự đại diện '*'. Ngoài ra, bất kỳ số nào trong số này (và bất kỳ số nào trước đó.) Có thể bị thiếu.

Vì vậy, những điều sau là hợp lệ và phân tích cú pháp như sau:

1.23.456 = version 1, release 23, modification 456
1.23     = version 1, release 23, any modification
1.23.*   = version 1, release 23, any modification
1.*      = version 1, any release, any modification
1        = version 1, any release, any modification
*        = any version, any release, any modification

Nhưng những điều này không hợp lệ:

*.12
*123.1
12*
12.*.34

Có ai có thể cung cấp cho tôi một regex không quá phức tạp để xác thực và truy xuất số phát hành, phiên bản và sửa đổi không?


Tôi không chắc một cái "đơn giản" là có thể.
svrist

Câu trả lời:


96

Tôi muốn thể hiện định dạng là:

"1-3 thành phần được phân tách bằng dấu chấm, mỗi thành phần là số ngoại trừ thành phần cuối cùng có thể là *"

Là một regexp, đó là:

^(\d+\.)?(\d+\.)?(\*|\d+)$

[Chỉnh sửa để thêm: giải pháp này là một cách ngắn gọn để xác thực, nhưng nó đã được chỉ ra rằng việc trích xuất các giá trị đòi hỏi phải làm việc thêm. Vấn đề là bạn nên giải quyết vấn đề này bằng cách làm phức tạp regexp hay bằng cách xử lý các nhóm đã khớp.

Trong giải pháp của tôi, các nhóm nắm bắt các "."nhân vật. Điều này có thể được giải quyết bằng cách sử dụng các nhóm không bắt như trong câu trả lời của ajborley.

Ngoài ra, nhóm ngoài cùng bên phải sẽ nắm bắt thành phần cuối cùng, ngay cả khi có ít hơn ba thành phần, và do đó, ví dụ: kết quả đầu vào hai thành phần trong nhóm đầu tiên và nhóm cuối cùng chụp và nhóm ở giữa không được xác định. Tôi nghĩ điều này có thể được giải quyết bởi các nhóm không tham lam nếu được hỗ trợ.

Mã Perl để giải quyết cả hai vấn đề sau regexp có thể giống như sau:

@version = ();
@groups = ($1, $2, $3);
foreach (@groups) {
    next if !defined;
    s/\.//;
    push @version, $_;
}
($major, $minor, $mod) = (@version, "*", "*");

Điều đó không thực sự ngắn hơn việc chia nhỏ trên "." ]


1
Thêm một số nhóm không chụp (xem câu trả lời của tôi bên dưới) có nghĩa là các nhóm bắt không bắt được dấu vết '.' ^ (?: (\ d +) \.)? (?: (\ d +) \.)? (* | \ d +) $ Cảm ơn!
Andrew Borley 17-08

Vấn đề duy nhất với đề xuất đó - là một đề xuất rất đẹp và sạch sẽ - là các nhóm không đúng vì 1,2 sẽ bắt 1 trong nhóm đầu tiên và 2 trong nhóm thứ ba vì tham lam.
jrudolph 17/09/08

39

Sử dụng regex và bây giờ bạn có hai vấn đề. Tôi sẽ chia thứ trên dấu chấm ("."), Sau đó đảm bảo rằng mỗi phần là một ký tự đại diện hoặc tập hợp các chữ số (regex hiện là hoàn hảo). Nếu điều này hợp lệ, bạn chỉ cần trả về phần chính xác của phần tách.


11

Điều này có thể hoạt động:

^(\*|\d+(\.\d+){0,2}(\.\*)?)$

Ở cấp cao nhất, "*" là trường hợp đặc biệt của số phiên bản hợp lệ. Nếu không, nó bắt đầu bằng một số. Sau đó, không có, một hoặc hai chuỗi ".nn", theo sau là ". *" Tùy chọn. Regex này sẽ chấp nhận 1.2.3. * Có thể được phép hoặc không được phép trong ứng dụng của bạn.

Mã để truy xuất các trình tự phù hợp, đặc biệt là (\.\d+){0,2}phần, sẽ phụ thuộc vào thư viện regex cụ thể của bạn.


Câu trả lời chính xác! Tôi nghĩ bạn nên hoán đổi * không thoát cho {0,2} để ngăn chặn việc khớp 1.2.3.4. Tùy thuộc vào thư viện regexp của bạn, bạn có thể muốn bao gồm mẫu trong ^ (<pattern>) $ nếu bạn chỉ có thể thực hiện tìm kiếm thay vì so khớp.
Dave Webb

Thay đổi nhẹ thành ^ (* | \ d + (\. \ D +) {0,1} (?: (\. *)? | (\. \ D +)?)) $ Cũng sẽ làm mất hiệu lực 1.2.3. *
Pieter

2
Pieter: Tôi nghĩ bây giờ tôi sẽ dừng lại ở vị trí hiện tại. Điều này nhanh chóng đi vào lãnh thổ "bây giờ bạn có hai vấn đề". :)
Greg Hewgill 17/09/08

11

Cảm ơn vì tất cả những phản hồi! Đây là ace :)

Dựa trên câu trả lời của OneByOne (trông đơn giản nhất đối với tôi), tôi đã thêm một số nhóm không chụp (phần '(?:' - cảm ơn VonC đã giới thiệu tôi với các nhóm không chụp!), Vì vậy các nhóm chỉ chụp chứa các chữ số hoặc ký tự *.

^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

Rất cám ơn mọi người!


1
Thay vào đó, bạn có thể thêm điều này làm chỉnh sửa cho câu hỏi của mình không? Bằng cách đó, câu trả lời đúng gần với đầu trang nhất
svrist

1
Với tên nhóm: ^ (? :(? <major> \ d +) \.)? (?
:(

1
hỗ trợ semversion (nhiều hơn một chút). - "1.2.3-alpha + abcdedf.lalal" -match "^ (?: (\ D +) \.)? (?: (\ D +) \.)? (* | \ D +)? (?: \ - ([A-Za-z0-9 \.] +))? (?: \ + ([A-Za-z0-9 \.] +))? $ "
Sam

Hãy lưu ý rằng trong trường hợp một phiên bản bao gồm một số duy nhất, nó sẽ được khớp với nhóm thứ ba (\*|\d+)không phải ^(?:(\d+)\.)?nhóm đầu tiên .
Piotr Dobrogost

8

2 xu của tôi: Tôi đã gặp trường hợp này: Tôi phải phân tích cú pháp số phiên bản ra khỏi một chuỗi ký tự. (Tôi biết điều này rất khác với câu hỏi ban đầu, nhưng googling để tìm regex để phân tích cú pháp số phiên bản đã hiển thị chuỗi này ở trên cùng, vì vậy hãy thêm câu trả lời này ở đây)

Vì vậy, chuỗi ký tự sẽ giống như: "Phiên bản dịch vụ 1.2.35.564 đang chạy!"

Tôi đã phải phân tích cú pháp 1.2.35.564 ra khỏi nghĩa đen này. Lấy gợi ý từ @ajborley, regex của tôi như sau:

(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)

Một đoạn mã C # nhỏ để kiểm tra điều này trông giống như bên dưới:

void Main()
{
    Regex regEx = new Regex(@"(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)", RegexOptions.Compiled);

    Match version = regEx.Match("The Service SuperService 2.1.309.0) is Running!");
    version.Value.Dump("Version using RegEx");   // Prints 2.1.309.0        
}

Tôi biết bạn đang mô tả một tình huống và trường hợp thay thế, nhưng chỉ cần hoàn chỉnh: SemVer 'yêu cầu' chuỗi phiên bản phải có định dạng X.Y.Z(vì vậy, chính xác là ba phần), trong đó X và Y phải là số nguyên không âm và không bổ sung số không ở đầu. Xem semver.org .
Jochem Schulenklopper

1
@JochemSchulenklopper cảm ơn, tôi biết về SemVer, mặc dù câu hỏi không đề cập gì đến SemVer.
Sudhanshu Mishra

1
Thật. Tôi đã được một đồng nghiệp giới thiệu câu hỏi này về cách phân tích chuỗi SemVer, vì vậy điều đó đã đóng khung việc đọc các câu trả lời của tôi.
Jochem Schulenklopper

7

Không biết bạn đang sử dụng nền tảng nào nhưng trong .NET có lớp System.Version sẽ phân tích cú pháp số phiên bản "nnnn" cho bạn.


Không, nó đã có từ phiên bản 1.0
Duncan Smart

5

Tôi có xu hướng đồng ý với đề xuất chia tách.

Ive đã tạo một "người kiểm tra" cho vấn đề của bạn trong perl

#!/usr/bin/perl -w


@strings = ( "1.2.3", "1.2.*", "1.*","*" );

%regexp = ( svrist => qr/(?:(\d+)\.(\d+)\.(\d+)|(\d+)\.(\d+)|(\d+))?(?:\.\*)?/,
            onebyone => qr/^(\d+\.)?(\d+\.)?(\*|\d+)$/,
            greg => qr/^(\*|\d+(\.\d+){0,2}(\.\*)?)$/,
            vonc => qr/^((?:\d+(?!\.\*)\.)+)(\d+)?(\.\*)?$|^(\d+)\.\*$|^(\*|\d+)$/,
            ajb => qr/^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$/,
            jrudolph => qr/^(((\d+)\.)?(\d+)\.)?(\d+|\*)$/
          );

  foreach my $r (keys %regexp){
    my $reg = $regexp{$r};
    print "Using $r regexp\n";
foreach my $s (@strings){
  print "$s : ";

    if ($s =~m/$reg/){
    my ($main, $maj, $min,$rev,$ex1,$ex2,$ex3) = ("any","any","any","any","any","any","any");
    $main = $1 if ($1 && $1 ne "*") ;
    $maj = $2 if ($2 && $2 ne "*") ;
    $min = $3 if ($3 && $3 ne "*") ;
    $rev = $4 if ($4 && $4 ne "*") ;
    $ex1 = $5 if ($5 && $5 ne "*") ;
    $ex2 = $6 if ($6 && $6 ne "*") ;
    $ex3 = $7 if ($7 && $7 ne "*") ;
    print "$main $maj $min $rev $ex1 $ex2 $ex3\n";

  }else{
  print " nomatch\n";
  }
  }
print "------------------------\n";
}

Sản lượng hiện tại:

> perl regex.pl
Using onebyone regexp
1.2.3 : 1. 2. 3 any any any any
1.2.* : 1. 2. any any any any any
1.* : 1. any any any any any any
* : any any any any any any any
------------------------
Using svrist regexp
1.2.3 : 1 2 3 any any any any
1.2.* : any any any 1 2 any any
1.* : any any any any any 1 any
* : any any any any any any any
------------------------
Using vonc regexp
1.2.3 : 1.2. 3 any any any any any
1.2.* : 1. 2 .* any any any any
1.* : any any any 1 any any any
* : any any any any any any any
------------------------
Using ajb regexp
1.2.3 : 1 2 3 any any any any
1.2.* : 1 2 any any any any any
1.* : 1 any any any any any any
* : any any any any any any any
------------------------
Using jrudolph regexp
1.2.3 : 1.2. 1. 1 2 3 any any
1.2.* : 1.2. 1. 1 2 any any any
1.* : 1. any any 1 any any any
* : any any any any any any any
------------------------
Using greg regexp
1.2.3 : 1.2.3 .3 any any any any any
1.2.* : 1.2.* .2 .* any any any any
1.* : 1.* any .* any any any any
* : any any any any any any any
------------------------

Điều đó thật tuyệt, vì OneByOne có vẻ như là người đơn giản nhất.
jrudolph 17/09/08

Bạn cũng nên kiểm tra những cái sai. Bạn đã lỡ trích dẫn các dấu chấm của OneByOne.
jrudolph 17/09/08

Cập nhật các dấu chấm và nhiều regexps khác
svrist

4

Điều này sẽ hoạt động cho những gì bạn đã quy định. Nó nằm ở vị trí thẻ đại diện và là một regex lồng nhau:

^((\*)|([0-9]+(\.((\*)|([0-9]+(\.((\*)|([0-9]+)))?)))?))$

http://imgur.com/3E492.png


4

Tôi đã thấy rất nhiều câu trả lời, nhưng ... tôi có một câu trả lời mới. Nó hoạt động cho tôi ít nhất. Tôi đã thêm một hạn chế mới. Số phiên bản không được bắt đầu (chính, phụ hoặc bản vá) với bất kỳ số 0 nào được theo sau bởi các số khác.

01.0.0 không hợp lệ 1.0.0 là hợp lệ 10.0.10 là hợp lệ 1.0.0000 không hợp lệ

^(?:(0\\.|([1-9]+\\d*)\\.))+(?:(0\\.|([1-9]+\\d*)\\.))+((0|([1-9]+\\d*)))$

Nó dựa trên một cái trước đó. Nhưng tôi thấy giải pháp này tốt hơn ... cho tôi;)

Thưởng thức!!!


3

Một lần thử khác:

^(((\d+)\.)?(\d+)\.)?(\d+|\*)$

Điều này cho ba phần trong các nhóm 4,5,6 NHƯNG: Chúng được căn chỉnh về bên phải. Vì vậy, một trong số 4,5 hoặc 6 khác không đầu tiên cung cấp cho trường phiên bản.

  • 1.2.3 cho 1,2,3
  • 1,2. * Cho 1,2, *
  • 1,2 cho giá trị null, 1,2
  • *** cho null, null, *
  • 1. * cho giá trị null, 1, *

3
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

Có lẽ một cách ngắn gọn hơn có thể là:

^(?:(\d+)\.){0,2}(\*|\d+)$

Sau đó, điều này có thể được nâng cao lên 1.2.3.4.5. * Hoặc bị giới hạn chính xác đối với XYZ bằng cách sử dụng * hoặc {2} thay vì {0,2}


3

Tôi có một yêu cầu để tìm kiếm / đối sánh số phiên bản tuân theo quy ước maven hoặc thậm chí chỉ là một chữ số. Nhưng không có vòng loại trong mọi trường hợp. Thật là kỳ lạ, phải mất thời gian sau đó tôi mới nghĩ ra điều này:

'^[0-9][0-9.]*$'

Điều này đảm bảo rằng phiên bản,

  1. Bắt đầu bằng một chữ số
  2. Có thể có bất kỳ số chữ số nào
  3. Chỉ các chữ số và '.' được cho phép

Một nhược điểm là phiên bản thậm chí có thể kết thúc bằng '.' Nhưng nó có thể xử lý độ dài phiên bản vô thời hạn (lập phiên bản điên rồ nếu bạn muốn gọi nó như vậy)

Diêm:

  • 1.2.3
  • 1,09,5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Nếu bạn không hài lòng với '.' kết thúc, có thể bạn có thể kết hợp với logic kết thúc


Để thoát khỏi chữ số cuối cùng, có lẽ bạn muốn thử điều này:(\d+)(.\d+)*
cassioso

2
(?ms)^((?:\d+(?!\.\*)\.)+)(\d+)?(\.\*)?$|^(\d+)\.\*$|^(\*|\d+)$

Đối sánh chính xác với 6 ví dụ đầu tiên của bạn và từ chối 4 ví dụ khác

  • nhóm 1: major hoặc major.minor hoặc '*'
  • nhóm 2 nếu tồn tại: trẻ vị thành niên hoặc *
  • nhóm 3 nếu tồn tại: *

Bạn có thể xóa '(? Ms)'
Tôi đã sử dụng nó để biểu thị cho regexp này sẽ được áp dụng trên nhiều dòng thông qua QuickRex


2

Điều này cũng khớp với 1.2.3. *

^ (* | \ d + (. \ d +) {0,2} (. *)?) $

Tôi sẽ đề xuất ít thanh lịch hơn:

(* | \ d + (. \ d +)? (. *)?) | \ d +. \ d +. \ d +)


2

Hãy nhớ rằng regexp rất tham lam, vì vậy nếu bạn chỉ đang tìm kiếm trong chuỗi số phiên bản chứ không phải trong một văn bản lớn hơn, hãy sử dụng ^ và $ để đánh dấu bắt đầu và kết thúc chuỗi của bạn. Regexp từ Greg có vẻ hoạt động tốt (chỉ cần thử nhanh trong trình chỉnh sửa của tôi), nhưng tùy thuộc vào thư viện / ngôn ngữ của bạn, phần đầu tiên vẫn có thể khớp với "*" trong số phiên bản sai. Có lẽ tôi đang thiếu một cái gì đó, vì tôi đã không sử dụng Regexp trong một năm hoặc lâu hơn.

Điều này sẽ đảm bảo rằng bạn chỉ có thể tìm thấy số phiên bản chính xác:

^ (\ * | \ d + (\. \ d +) * (\. \ *)?) $

chỉnh sửa: thực sự greg đã thêm chúng rồi và thậm chí còn cải thiện giải pháp của anh ấy, tôi quá chậm :)


2

Có vẻ như khá khó để có một regex thực hiện chính xác những gì bạn muốn (tức là chỉ chấp nhận các trường hợp bạn cần và từ chối tất cả các trường hợp khác trả lại một số nhóm cho ba thành phần). Tôi đã thử và nghĩ ra điều này:

^(\*|(\d+(\.(\d+(\.(\d+|\*))?|\*))?))$

IMO (tôi chưa thử nghiệm rộng rãi) điều này sẽ hoạt động tốt như một trình xác thực cho đầu vào, nhưng vấn đề là regex này không cung cấp cách truy xuất các thành phần. Vì vậy, bạn vẫn phải thực hiện phân chia theo chu kỳ.

Giải pháp này không phải là tất cả trong một, nhưng hầu hết trong lập trình, nó không cần. Tất nhiên điều này phụ thuộc vào các hạn chế khác mà bạn có thể có trong mã của mình.


2

Chỉ định các phần tử XSD:

<xs:simpleType>
    <xs:restriction base="xs:string">
        <xs:pattern value="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(\..*)?"/>
    </xs:restriction>
</xs:simpleType>

2

Việc này của tôi, như một bài tập tốt - vparse , có một nguồn nhỏ , với một chức năng đơn giản:

function parseVersion(v) {
    var m = v.match(/\d*\.|\d+/g) || [];
    v = {
        major: +m[0] || 0,
        minor: +m[1] || 0,
        patch: +m[2] || 0,
        build: +m[3] || 0
    };
    v.isEmpty = !v.major && !v.minor && !v.patch && !v.build;
    v.parsed = [v.major, v.minor, v.patch, v.build];
    v.text = v.parsed.join('.');
    return v;
}

2

Để phân tích cú pháp số phiên bản tuân theo các quy tắc sau: - Chỉ là chữ số và dấu chấm - Không thể bắt đầu hoặc kết thúc bằng dấu chấm - Không thể là hai dấu chấm cùng nhau

Cái này đã lừa tôi.

^(\d+)((\.{1}\d+)*)(\.{0})$

Các trường hợp hợp lệ là:

1, 0,1, 1.2.1



1

Đôi khi số phiên bản có thể chứa thông tin nhỏ gồm chữ và số (ví dụ: 1.2.0b hoặc 1.2.0-beta ). Trong trường hợp này, tôi đang sử dụng regex này:

([0-9]{1,4}(\.[0-9a-z]{1,6}){1,5})

0

Tôi đã tìm thấy cái này và nó phù hợp với tôi:

/(\^|\~?)(\d|x|\*)+\.(\d|x|\*)+\.(\d|x|\*)+
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.