버전 번호 구문 분석을위한 정규식


84

다음 형식의 버전 번호가 있습니다.

version.release.modification

여기서 버전, 릴리스 및 수정은 숫자 세트 또는 '*'와일드 카드 문자입니다. 또한 이러한 숫자 (및 앞의 모든.)가 누락 될 수 있습니다.

따라서 다음은 유효하며 다음과 같이 구문 분석됩니다.

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

그러나 이들은 유효하지 않습니다.

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

누구든지 릴리스, 버전 및 수정 번호를 확인하고 검색하기 위해 너무 복잡하지 않은 정규식을 제공 할 수 있습니까?


"간단한"것이 가능한지 잘 모르겠습니다.
svrist

답변:


96

형식을 다음과 같이 표현하겠습니다.

"1-3 개의 점으로 구분 된 구성 요소, 마지막 구성 요소를 제외한 각 숫자는 * 일 수 있음"

정규 표현식 으로서는 다음과 같습니다.

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

[추가하려면 편집 :이 솔루션은 간결한 유효성 검사 방법이지만 값을 추출하려면 추가 작업이 필요하다는 점이 지적되었습니다. 정규 표현식을 복잡하게하거나 일치하는 그룹을 처리하여이를 처리할지 여부는 취향의 문제입니다.

내 솔루션에서 그룹은 "."캐릭터를 캡처합니다 . 이것은 ajborley의 답변에서와 같이 비 캡처 그룹을 사용하여 처리 할 수 ​​있습니다.

또한 가장 오른쪽 그룹은 세 개 미만의 구성 요소가 있더라도 마지막 구성 요소를 캡처하므로 예를 들어 두 구성 요소 입력은 첫 번째 및 마지막 그룹이 캡처되고 중간 그룹은 정의되지 않습니다. 나는 욕심이없는 그룹이 지원을 받으면 처리 할 수 ​​있다고 생각합니다.

정규 표현식 이후에 두 문제를 모두 처리하는 Perl 코드는 다음과 같습니다.

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

쪼개지는 것보다 더 짧지는 않네요 "." ]


1
일부 비 캡처 그룹을 추가하면 (아래 내 대답 참조) 캡처 그룹이 후행 '.'을 캡처하지 않습니다. ^ (? : (\ d +) \.)? (? : (\ d +) \.)? (* | \ d +) $ 감사합니다!
Andrew Borley

그 하나의 유일한 문제는-매우 훌륭하고 깨끗한 제안-1.2가 욕심 때문에 첫 번째 그룹에서 1, 세 번째 그룹에서 2를 차지하기 때문에 그룹이 옳지 않다는 것입니다.
jrudolph

39

정규식을 사용하면 이제 두 가지 문제가 있습니다. 점 ( ".")으로 분할 한 다음 각 부분이 와일드 카드 또는 숫자 집합인지 확인합니다 (정규식은 이제 완벽합니다). 일이 유효하면 분할의 올바른 청크를 반환합니다.


11

이것은 작동 할 수 있습니다.

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

최상위 수준에서 "*"는 유효한 버전 번호의 특별한 경우입니다. 그렇지 않으면 숫자로 시작합니다. 그런 다음 0 개, 1 개 또는 2 개의 ".nn"시퀀스가 있고 그 뒤에 선택적 ". *"가 있습니다. 이 정규식은 응용 프로그램에서 허용되거나 허용되지 않을 수있는 1.2.3. *을 허용합니다.

일치하는 시퀀스, 특히 (\.\d+){0,2}부분 을 검색하는 코드는 특정 정규식 라이브러리에 따라 다릅니다.


좋은 대답! 1.2.3.4 일치를 방지하려면 이스케이프 처리되지 않은 *를 {0,2}로 바꿔야한다고 생각합니다. 정규식 라이브러리에 따라 일치하는 것이 아니라 검색 만 수행 할 수있는 경우 패턴을 ^ (<pattern>) $로 묶을 수 있습니다.
Dave Webb

^ (* | \ d + (\. \ d +) {0,1} (? : (\. *)? | (\. \ d +)?)) $를 약간 변경하면 1.2.3. *도 무효화됩니다.
Pieter

2
피터 : 지금은 제가있는 곳에서 그만 둘 것 같아요. 이것은 "이제 두 가지 문제가 있습니다"영역으로 빠르게 진입하고 있습니다. :)
Greg Hewgill

11

모든 응답에 감사드립니다! 에이스입니다 :)

OneByOne의 답변 (가장 간단 해 보임)을 기반으로 일부 비 캡처 그룹 ( '(? :'부분-비 캡처 그룹을 소개 해준 VonC 덕분에!)을 추가했습니다. 숫자 또는 * 문자를 포함합니다.

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

모두에게 감사합니다!


1
대신 이것을 질문에 대한 편집으로 추가 할 수 있습니까? 이렇게하면 정답이 정상에 가깝습니다
svrist

1
그룹 이름 사용 : ^ (? : (? <major> \ d +) \.)? (? : (? <minor> \ d +) \.)? (? <build> * | \ d +) $
javacavaj

1
semversion을 지원합니다 (조금 더). - "1.2.3-alpha + abcdedf.lalal"-match "^ (? : (\ d +) \.)? (? : (\ d +) \.)? (* | \ d +)? (? : \- ([A-Za-z0-9 \.] +))? (? : \ + ([A-Za-z0-9 \.] +))? $ "
Sam

단일 숫자로 구성된 버전의 경우 (\*|\d+)첫 번째 ^(?:(\d+)\.)?그룹이 아닌 세 번째 숫자와 일치합니다 .
Piotr Dobrogost

8

내 2 센트 :이 시나리오가 있었다 : 문자열 리터럴에서 버전 번호를 구문 분석해야했습니다. (나는 이것이 원래 질문과 매우 다르다는 것을 알고 있지만 버전 번호를 구문 분석하기위한 정규식을 찾으려면 인터넷 검색 에서이 스레드가 맨 위에 표시되었으므로 여기 에이 답변을 추가하십시오)

따라서 문자열 리터럴은 다음과 같습니다. "서비스 버전 1.2.35.564가 실행 중입니다!"

이 리터럴에서 1.2.35.564를 구문 분석해야했습니다. @ajborley에서 신호를 받으면 내 정규식은 다음과 같습니다.

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

이를 테스트하기위한 작은 C # 스 니펫은 다음과 같습니다.

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        
}

나는 당신이 대체 상황과 사례를 설명하고 있다는 것을 알고 있지만, 완벽하게하기 위해서 : SemVer는 버전 문자열이 형식 X.Y.Z(정확히 세 부분)이어야하는데, 여기서 X와 Y는 음이 아닌 정수 여야하고 추가 선행 0. semver.org를 참조하십시오 .
Jochem Schulenklopper

1
@JochemSchulenklopper 감사합니다, SemVer에 대해 알고 있지만 질문에는 SemVer에 대한 내용이 없습니다.
Sudhanshu Mishra

1
진실. SemVer 문자열을 구문 분석하는 것에 대해 동료가이 질문을 참조하여 답변을 읽었습니다.
Jochem Schulenklopper

7

어떤 플랫폼을 사용하고 있는지 모르지만 .NET에는 "nnnn"버전 번호를 구문 분석하는 System.Version 클래스가 있습니다.


아니, 버전 1.0 이후가있었습니다
던컨 스마트에게

5

나는 분할 제안에 동의하는 경향이 있습니다.

펄에서 문제에 대한 "테스터"를 만들었습니다.

#!/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";
}

전류 출력 :

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

OneByOne이 가장 간단 해 보이기 때문에 좋을 것입니다.
jrudolph

잘못된 것도 테스트해야합니다. OneByOne의 점을 인용하지 못했습니다.
jrudolph

점과 더 많은
정규 표현식으로

4

이것은 당신이 규정 한대로 작동합니다. 와일드 카드 위치에 따라 달라지며 중첩 된 정규식입니다.

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

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


4

많은 답변을 봤는데 ... 새로운 답변이 있습니다. 적어도 나를 위해 작동합니다. 새로운 제한을 추가했습니다. 버전 번호는 0이 뒤에 오는 다른 것으로 시작 (주, 부 또는 패치) 할 수 없습니다.

01.0.0이 유효하지 않음 1.0.0이 유효 함 10.0.10이 유효 함 1.0.0000이 유효하지 않음

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

이전 버전을 기반으로합니다. 그러나 나는이 해결책을 더 잘 볼 수 있습니다 ... 나를 위해;)

즐겨!!!


3

또 다른 시도 :

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

이것은 그룹 4,5,6의 세 부분을 제공하지만 오른쪽에 정렬됩니다. 따라서 4,5 또는 6 중 첫 번째 null이 아닌 것은 버전 필드를 제공합니다.

  • 1.2.3은 1,2,3을 제공합니다.
  • 1.2. *는 1,2, *를 제공합니다.
  • 1.2는 null, 1,2를 제공합니다.
  • *** null, null, * 제공
  • 1. *는 null, 1, *를 제공합니다.

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

아마도 더 간결한 것은 다음과 같습니다.

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

그런 다음 1.2.3.4.5. *로 개선하거나 {0,2} 대신 * 또는 {2}를 사용하여 XYZ로 정확히 제한 할 수 있습니다.


3

나는 Maven 규칙 또는 심지어 한 자릿수를 따르는 버전 번호를 검색 / 일치해야한다는 요구 사항이있었습니다. 그러나 어떤 경우에도 한정자는 없습니다. 그것은 독특했고 시간이 걸렸고 나는 이것을 생각해 냈습니다.

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

이것은 버전을 확인합니다.

  1. 숫자로 시작
  2. 임의의 숫자를 가질 수 있습니다.
  3. 숫자와 '.'만 허용됩니다

한 가지 단점은 버전이 '.'로 끝날 수도 있다는 것입니다. 그러나 그것은 무한한 길이의 버전을 처리 할 수 ​​있습니다.

성냥:

  • 1.2.3
  • 1.09.5
  • 3.4.4.5.7.8.8.
  • 23.6.209.234.3

'.'이 마음에 들지 않으면 결말, 끝과 논리를 결합 할 수 있습니다.


: 마지막 숫자 제거하기 위해, 어쩌면 당신이 시도하고 싶습니다(\d+)(.\d+)*
cassioso

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

6 개의 첫 번째 예와 정확히 일치하고 나머지 4 개는 거부합니다.

  • 그룹 1 : major 또는 major.minor 또는 '*'
  • 그룹 2 (존재하는 경우) : 부 또는 *
  • 그룹 3 (존재하는 경우) : *

QuickRex를
통해 여러 줄에 적용되도록이 정규 표현식에 표시하는 데 사용한 '(? ms)'를 제거 할 수 있습니다.


2

이것은 1.2.3. * 과도 일치합니다.

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

덜 우아한 것을 제안합니다.

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


2

regexp는 탐욕 스럽기 때문에 더 큰 텍스트가 아닌 버전 번호 문자열 내에서 검색하는 경우 ^ 및 $를 사용하여 문자열의 시작과 끝을 표시하십시오. Greg의 정규식은 잘 작동하는 것 같지만 (제 편집기에서 간단히 시도해 보았습니다) 라이브러리 / 언어에 따라 첫 번째 부분이 잘못된 버전 번호 내에서 "*"와 일치 할 수 있습니다. Regexp를 1 년 정도 사용하지 않았기 때문에 뭔가 빠졌을 수도 있습니다.

이렇게하면 올바른 버전 번호 만 찾을 수 있습니다.

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

편집 : 실제로 그렉은 이미 그들을 추가했고 심지어 그의 솔루션을 개선했지만 너무 느립니다. :)


2

(즉, 그건 당신이 필요 단지의 경우를 수용하고 거부 정확하게 당신이 원하는 무엇을 정규식이 꽤 어려운 것 같다 모든 다른 사람 세 가지 구성 요소에 대한 몇 가지 그룹을 반환). 나는 그것을 시도하고 이것을 생각해 냈습니다.

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

IMO (내가 광범위하게 테스트하지 않았습니다) 이것은 입력에 대한 유효성 검사기로 잘 작동하지만 문제는이 정규식이 구성 요소를 검색하는 방법을 제공하지 않는다는 것입니다. 이를 위해 여전히 기간 분할을 수행해야합니다.

이 솔루션은 올인원이 아니지만 프로그래밍에서 대부분의 경우 필요하지 않습니다. 물론 이것은 코드에있을 수있는 다른 제한 사항에 따라 다릅니다.


2

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

이것은 간단한 기능 으로 작은 소스 를 가지고있는 vparse입니다 .

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

다음 규칙을 따르는 버전 번호를 구문 분석하는 경우 :-숫자와 점만 있음-점으로 시작하거나 끝날 수 없음-함께 두 개의 점이 될 수 없음

이것은 나에게 속임수를 쓴다.

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

유효한 경우는 다음과 같습니다.

1, 0.1, 1.2.1



1

때로는 버전 번호에 영숫자 부 정보 (예 : 1.2.0b 또는 1.2.0-beta ) 가 포함될 수 있습니다 . 이 경우 다음 정규식을 사용하고 있습니다.

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

0

나는 이것을 발견했고 그것은 나를 위해 작동합니다.

/(\^|\~?)(\d|x|\*)+\.(\d|x|\*)+\.(\d|x|\*)+
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.