2008-09-17 26 views
61

Tôi có một số phiên bản của hình thức sau đây:Một regex cho số phiên bản phân tích

version.release.modification

nơi phiên bản, phát hành và sửa đổi hoặc là một tập hợp các chữ số hoặc '*' wildcard tính cách. 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, sau đây là hợp lệ và phân tích như:

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 đây không phải là hợp lệ:

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

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

+0

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

Trả lời

51

tôi muốn bày tỏ các định dạng như:

"1-3 thành phần dot-tách ra, mỗi số ngoại trừ người cuối cùng có thể được *"

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 súc tích để xác nhận, nhưng nó đã được chỉ ra rằng chiết xuất các giá trị đòi hỏi thêm công việc. Đó là một vấn đề của hương vị cho dù để đối phó với điều này bằng cách phức tạp regexp, hoặc bằng cách xử lý các nhóm phù hợp.

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

Ngoài ra, nhóm ngoài cùng bên phải sẽ chụp 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 được chụp và phần giữa không được xác định. Tôi nghĩ rằng điều này có thể được xử lý bởi các nhóm không tham lam, nơi được hỗ trợ.

đang

Perl để đối phó với cả hai vấn đề sau khi regexp có thể là một cái gì đó như thế này:

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

Mà không phải là thực sự bất kỳ ngắn hơn tách trên "." ]

+0

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

+0

Vấn đề duy nhất với vấn đề đó - là một đề xuất rất hay và sạch - đó là các nhóm không đúng vì 1,2 sẽ thu được 1 trong nhóm đầu tiên và 2 trong nhóm thứ ba vì tham lam. – jrudolph

10

này có thể làm việc:

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

Ở cấp độ đầu, "*" là một trường hợp đặc biệt của một 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 ".nn" trình tự, theo sau là một tùy chọn ". *". Regex này chấp nhận 1.2.3. * Có thể hoặc không được phép trong đơn của bạn.

Mã để truy xuất các chuỗi trùng khớp, đặc biệt là phần (\.\d+){0,2}, sẽ tùy thuộc vào thư viện regex cụ thể của bạn.

+0

Câu trả lời hay! Tôi nghĩ bạn nên hoán đổi dấu * không cho phép {0,2} để ngăn chặn kết hợ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 đính kèm mẫu ở^() $ nếu bạn chỉ có thể thực hiện tìm kiếm thay vì đối sánh. –

+0

Điểm tốt, tôi đã cải thiện câu trả lời. –

+0

Thay đổi nhỏ thành^(\ * | \ d + (\. \ D +) {0,1} (?: (\. \ *)? | (\. \ D +)?)) $ Sẽ làm mất hiệu lực 1.2.3.* quá – Pieter

36

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

1

Hãy nhớ regexp là tham lam , vì vậy nếu bạn chỉ 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 soạn thảo 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ì đó, như tôi đã không sử dụng Regexp cho một năm hoặc lâu hơn.

này nên chắc chắn rằng bạn chỉ có thể tìm đúng số phiên bản:

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

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

+0

ouch vâng, không nhận thấy điều đó - cảm ơn :) – FrankS

4

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

Ive đã tạo ra một "thử nghiệm" 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 
------------------------ 
+0

Điều đó sẽ tốt đẹp, vì OneByOne trông giống như hình ảnh đơn giản nhất. – jrudolph

+0

Bạn cũng nên kiểm tra sai. Bạn đã bỏ lỡ dấu chấm của OneByOne. – jrudolph

+0

Cập nhật với các dấu chấm, và nhiều hơn nữa regexps – svrist

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

Liệu chính xác phù hợp 6 ví dụ đầu tiên của bạn, và từ chối 4 người khác

  • nhóm 1: major hoặc major.minor hoặc ' *'
  • nhóm 2 nếu tồn tại: nhỏ hoặc *
  • nhóm 3 nếu tồn tại: *

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

5

Không biết bạn đang ở trên 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.

+1

Chỉ trong .Net Framework 4 ...: \ –

+0

Không, nó đã ở đó kể từ phiên bản 1.0 –

1

này phù hợp với 1.2.3 * quá

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

Tôi sẽ đề xuất càng ít thanh lịch:

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

9

Cảm ơn tất cả các câu trả lờ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 bắt (phần ':?:' - nhờ VonC giới thiệu tôi với các nhóm không bắt!), do đó, các nhóm chụp chỉ chứa các chữ số hoặc ký tự *.

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

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

+1

Bạn có thể thêm này như là một chỉnh sửa cho câu hỏi của bạn để thay thế? Bằng cách đó, câu trả lời đúng nằm gần đầu trang – svrist

+1

Với tên nhóm: ^ (? :(? \ d +) \.)? (? :(? \ d +) \.)? (? \ * | \ d +) $ – javacavaj

+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

2

Một thử:

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

này mang lại cho ba phần trong nhóm 4,5,6 NHƯNG: Chúng được liên kết ở bên phải. Vì vậy, cái đầu tiên không null là 4,5 hoặc 6 cho trường phiên bản.

  • 1.2.3 cho 1,2,3
  • 1.2. * Cung cấp cho 1,2, *
  • 1.2 cho null, 1,2
  • *** cho null, null, *
  • 1. * cho null, 1, *
1

có vẻ như khá khó khăn để có một regex mà 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 mà bạn cần và từ chối tất cả khác trả lại một số nhóm cho ba thành phần). Tôi đã cho nó một thử và đưa ra với điều này:

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

IMO (Tôi đã không thử nghiệm rộng rãi) này sẽ làm việc tốt như một validator cho đầu vào, nhưng vấn đề là regex này không cung cấp một cách để lấy các thành phần. Cho rằng bạn vẫn phải làm một phân chia về thời gian.

Giải pháp này không phải là tất cả-trong-một, nhưng hầu hết thời gian trong lập trình mà 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 bạn.

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

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

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

này sau đó có thể được tăng cường để 1.2.3.4.5 * hoặc bị hạn chế một cách chính xác để XYZ sử dụng * hoặc {2} thay vì {0. , 2}

3

Điều này sẽ phù hợp với những gì bạn đã quy định. Nó xoay quanh vị trí thẻ hoang dã và là một regex lồng nhau:

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

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

2

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

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

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

Nó có trụ sở tại một 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 !!!

4

2 xu của tôi: Tôi có kịch bản này: Tôi phải phân tích cú pháp số phiên bản ra khỏi chuỗi chữ. (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 một regex để phân tích số phiên bản cho thấy chuỗi này ở trên cùng, do đó, thêm câu trả lời này tại đây)

Vì vậy, chuỗi chữ sẽ giống như sau: 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 trong số này. Với sự gợi ý từ @ajborley, regex của tôi là như sau:

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

Một C nhỏ đoạn # để kiểm tra điều này có vẻ như dưới đây:

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   
} 
+0

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

+1

@JochemSchulenklopper cảm ơn, tôi biết SemVer, mặc dù câu hỏi không đề cập đến bất cứ điều gì về SemVer. – dotnetguy

+1

Đúng. Tôi đã được một đồng nghiệp giới thiệu về phân tích chuỗi SemVer, vì vậy mà tôi đã đọc được câu trả lời. –

1

Thêm một giải pháp:

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

Quy định rõ 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> 
1

về vấn đề này, như một bài tập tốt - vparse, trong đó có một tiny source, 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; 
} 
1

Tôi đã có một yêu cầu để tìm kiếm/phù hợp cho số phiên bản, mà sau ước maven hoặc thậm chí chữ số chỉ duy nhất. Nhưng không có vòng loại nào trong mọi trường hợp. Đó là đặc biệt, nó đã cho tôi thời gian sau đó tôi đến với điều này:

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

Điều này đảm bảo các phiên bản,

  1. Bắt đầu bằng một chữ số
  2. có thể có bất kỳ số lượng chữ số
  3. Chỉ có chữ số và '.' được 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ý chiều dài vô thời hạn của phiên bản (versioning điên nếu bạn muốn gọi nó đó)

Matches:

  • 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ể là bạn có thể kết hợp với logic endswith

+0

Để loại bỏ chữ số cuối cùng, có thể bạn muốn thử cái này: '(\ d +) (. \ D +) *' – cassioso

Các vấn đề liên quan