it-swarm-id.com

Regex untuk parsing nomor versi

Saya memiliki nomor versi dari formulir berikut:

version.release.modification

di mana versi, rilis, dan modifikasi adalah sekumpulan digit atau karakter wildcard '*'. Selain itu, nomor-nomor ini (dan nomor sebelumnya) mungkin tidak ada.

Jadi yang berikut ini valid dan parse sebagai:

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

Tetapi ini tidak valid:

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

Adakah yang bisa memberi saya regex yang tidak terlalu rumit untuk memvalidasi dan mengambil nomor rilis, versi, dan modifikasi?

73
Andrew Borley

Saya akan menyatakan format sebagai:

"1-3 komponen yang dipisahkan titik, masing-masing numerik kecuali yang terakhir mungkin *"

Sebagai regexp, itu:

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

[Sunting untuk menambahkan: solusi ini adalah cara ringkas untuk memvalidasi, tetapi telah ditunjukkan bahwa mengekstraksi nilai membutuhkan kerja ekstra. Ini masalah selera apakah untuk menangani ini dengan menyulitkan regexp, atau dengan memproses kelompok yang cocok.

Dalam solusi saya, grup menangkap "." karakter. Ini dapat diatasi dengan menggunakan grup yang tidak menangkap seperti pada jawaban ajborley.

Juga, kelompok paling kanan akan menangkap komponen terakhir, bahkan jika ada kurang dari tiga komponen, dan jadi misalnya input dua komponen menghasilkan kelompok pertama dan terakhir menangkap dan yang tengah tidak terdefinisi. Saya pikir ini dapat ditangani oleh kelompok non-serakah di mana didukung.

Kode Perl untuk menangani kedua masalah setelah regexp bisa jadi seperti ini:

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

Yang tidak benar-benar lebih pendek daripada pemisahan pada "."]

76
Steve Jessop

Gunakan regex dan sekarang Anda memiliki dua masalah. Saya akan membagi semuanya pada titik-titik ("."), Kemudian pastikan bahwa setiap bagian adalah wildcard atau set angka (regex sudah sempurna sekarang). Jika hal itu valid, Anda hanya mengembalikan potongan split yang benar.

38
Paweł Hajdan

Ini mungkin berhasil:

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

Di tingkat atas, "*" adalah huruf khusus dari nomor versi yang valid. Kalau tidak, itu dimulai dengan angka. Lalu ada nol, satu, atau dua urutan ".nn", diikuti oleh opsional ". *". Regex ini akan menerima 1.2.3. * Yang mungkin atau mungkin tidak diizinkan dalam aplikasi Anda.

Kode untuk mengambil urutan yang cocok, terutama (\.\d+){0,2} bagian, akan tergantung pada pustaka regex khusus Anda.

11
Greg Hewgill

Terima kasih atas semua tanggapannya! Ini ace :)

Berdasarkan jawaban OneByOne (yang terlihat paling sederhana bagi saya), saya menambahkan beberapa kelompok yang tidak menangkap (bagian '(?:' - terima kasih kepada VonC karena telah memperkenalkan saya ke kelompok yang tidak menangkap!), Sehingga kelompok yang hanya menangkap hanya berisi digit atau karakter *.

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

Terima kasih banyak semuanya!

11
Andrew Borley

2 sen saya: Saya punya skenario ini: Saya harus mengurai nomor versi dari string literal. (Saya tahu ini sangat berbeda dari pertanyaan semula, tetapi googling untuk menemukan regex untuk nomor versi penguraian menunjukkan utas ini di bagian atas, jadi tambahkan jawaban ini di sini)

Jadi string literal akan menjadi seperti: "Versi layanan 1.2.35.564 sedang berjalan!"

Saya harus menguraikan 1.2.35.564 dari literal ini. Mengambil petunjuk dari @ajborley, regex saya adalah sebagai berikut:

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

Cuplikan C # kecil untuk menguji ini terlihat seperti di bawah ini:

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        
}
7
Sudhanshu Mishra

Tidak tahu platform apa yang Anda gunakan tetapi di. NET ada kelas System.Version yang akan menguraikan nomor versi "n.n.n.n" untuk Anda.

7
Duncan Smart

Saya cenderung setuju dengan saran split.

Ive membuat "tester" untuk masalah Anda di 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";
}

Keluaran saat ini:

> 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
------------------------
5
svrist

Ini harus bekerja untuk apa yang Anda tetapkan. Itu bergantung pada posisi wild card dan merupakan regex bersarang:

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

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

4
nomuus

Saya telah melihat banyak jawaban, tapi ... saya punya yang baru. Setidaknya itu bekerja untuk saya. Saya telah menambahkan batasan baru. Nomor versi tidak dapat dimulai (utama, kecil atau tambalan) dengan nol apa pun yang diikuti oleh orang lain.

01.0.0 tidak valid 1.0.0 valid 10.0.10 valid 1.0.0000 tidak valid

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

Berbasis di yang sebelumnya. Tetapi saya melihat solusi ini lebih baik ... untuk saya;)

Nikmati!!!

4
Israel Romero

Usaha lain:

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

Ini memberi tiga bagian dalam kelompok 4,5,6 TETAPI: Mereka disejajarkan ke kanan. Jadi yang non-nol pertama dari 4,5 atau 6 memberikan bidang versi.

  • 1.2.3 memberi 1,2,3
  • 1.2. * Memberi 1,2, *
  • 1.2 memberi null, 1,2
  • *** memberi null, null, *
  • 1. * memberi null, 1, *
3
jrudolph
^(?:(\d+)\.)?(?:(\d+)\.)?(\*|\d+)$

Mungkin yang lebih ringkas adalah:

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

Ini kemudian dapat ditingkatkan menjadi 1.2.3.4.5. * Atau dibatasi persis ke X.Y.Z menggunakan * atau {2} alih-alih {0,2}

3
ofaurax

Saya memiliki persyaratan untuk mencari/mencocokkan nomor versi, yang mengikuti konvensi maven atau bahkan hanya satu digit. Tapi tidak ada kualifikasi dalam hal apa pun. Itu aneh, saya butuh waktu kemudian saya datang dengan ini:

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

Ini memastikan versi,

  1. Mulai dengan angka
  2. Dapat memiliki jumlah digit apa pun
  3. Hanya digit dan '.' diijinkan

Salah satu kelemahannya adalah versi itu bahkan bisa diakhiri dengan '.' Tapi itu bisa menangani versi yang tidak terbatas (versi gila jika Anda ingin menyebutnya begitu)

Cocok:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

Jika Anda tidak senang dengan '.' ending, mungkin kamu bisa menggabungkan dengan endswith logic

3
Shiva

Tampaknya cukup sulit untuk memiliki regex yang melakukan persis apa yang Anda inginkan (yaitu hanya menerima kasus yang Anda butuhkan dan tolak semua lainnya dan mengembalikan beberapa grup untuk tiga komponen). Saya sudah mencobanya dan menghasilkan ini:

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

IMO (Saya belum diuji secara luas) ini harus berfungsi dengan baik sebagai validator untuk input, tetapi masalahnya adalah bahwa regex ini tidak menawarkan cara untuk mengambil komponen. Untuk itu Anda masih harus melakukan split secara berkala.

Solusi ini tidak all-in-one, tetapi seringkali pemrograman tidak perlu dilakukan. Tentu saja ini tergantung pada batasan lain yang mungkin Anda miliki dalam kode Anda.

2
rslite

Ingatlah bahwa regexp serakah, jadi jika Anda hanya mencari di dalam string nomor versi dan tidak dalam teks yang lebih besar, gunakan ^ dan $ untuk menandai awal dan akhir string Anda. Regexp dari Greg tampaknya berfungsi baik (coba saja di editor saya), tetapi tergantung pada pustaka/bahasa Anda, bagian pertama masih dapat mencocokkan "*" dengan nomor versi yang salah. Mungkin saya kehilangan sesuatu, karena saya belum pernah menggunakan Regexp selama kurang lebih satu tahun.

Ini harus memastikan Anda hanya dapat menemukan nomor versi yang benar:

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

sunting: sebenarnya greg menambahkannya dan bahkan memperbaiki solusinya, saya terlalu lambat :)

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

Sama persis dengan 6 contoh pertama Anda, dan tolak 4 lainnya

  • grup 1: mayor atau mayor. minor atau '*'
  • grup 2 jika ada: minor atau *
  • grup 3 jika ada: *

Anda dapat menghapus '(? Ms)'
Saya menggunakannya untuk menunjukkan kepada regexp ini untuk diterapkan pada multi-baris melalui QuickRex

2
VonC

Menentukan elemen 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
Emmerson

Ini cocok dengan 1.2.3. * Juga

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

Saya akan mengusulkan yang kurang elegan:

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

2
Victor

Pendapat saya tentang ini, sebagai latihan yang bagus - vparse , yang memiliki sumber kecil , dengan fungsi sederhana:

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
vitaly-t

Satu lagi solusi:

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