2012-09-01 5 views
5

다음과 같은 정규식이 있다고 가정 해 봅시다. 파일에서 변수 $ regex로로드 했으므로 런타임에 내용을 디자인 타임에 알 수 없습니다. 나는 그것이 "버전 1", "버전 2", "버전 3"와 "버전 4 '라는 이름의 그룹이 포함되어 있음을 발견 할 수 있습니다 :Powershell : 정규식 이름의 그룹을 변수로 바꾸기

"Version (?<version1>\d),(?<version2>\d),(?<version3>\d),(?<version4>\d)" 

을 ... 그리고 나는 이러한 변수를 가지고 :

$version1 = "3" 
$version2 = "2" 
$version3 = "1" 
$version4 = "0" 

을 .. . 그리고 파일의 다음 문자열을 발견했습니다.

Version 7,7,0,0 

... ($ input -match $ regex) 변수가 $ true로 평가되도록 변수 $ input에 저장됩니다.

$ regex에 나타나는 순서를 모르는 경우 어떻게 $ input 문자열의 $ regex에있는 명명 된 그룹을 $ version1, $ version2, $ version3, $ version4의 값으로 바꿀 수 있습니까? $ regex에는 이러한 명명 된 그룹이 포함됨을 알 수 있습니까?)

일치하는 색인으로 그룹 이름을 사용하여 이름이 지정된 그룹을 변수 값으로 대체하는 구문을 설명하는 참조를 찾을 수 없습니다. 심지어 지원됩니까?

편집 : 명확히하기 - 목표는 주어진 파일의 버전 문자열 버전 필드의 변수 번호의 교체를 필요로 텍스트 파일의 모든 종류의 템플릿 버전 문자열을 대체하는 것입니다 (2, 3이 될 수 또는 4 개의 필드 모두). 예를 들어, 파일의 텍스트는 이들의 모습 수있다 (그러나 이들에 제한되지 않는다) :

#define SOME_MACRO(4, 1, 0, 0) 

Version "1.2.3.4" 

SomeStruct vs = { 99,99,99,99 } 

사용자는 함께 필드를 포함하는 행과 일치하는 파일 세트와 정규 표현식을 지정할 수 있습니다 원래 아이디어는 개별 필드가 명명 된 그룹에 포착된다는 것입니다. 이 유틸리티에는 파일에서 대체해야하는 개별 버전 필드 값이 있지만 대체가 포함될 행의 원래 형식을 보존하고 요청 된 필드 만 대체해야합니다.

편집-2 : 나는 내가 성냥의 각각의 위치와 정도에 따라 문자열 계산에 필요로하는 결과를 얻을 수 있다고 생각하지만, 파워 쉘의 교체 작업이 좀 작업을 저장 거라고 기대했다.

편집-3 : 정확하고 간결하게 아래에 설명 안스로 그래서, 거기에만 원래의 입력 문자열, 당신은 단지라는 이름의 그룹을 알고있는에 대해 정규 표현식을 사용하는 방법은 (이 아니며, 그 결과 "-replace"연산 (또는 다른 regex 연산)을 사용하여 명명 된 그룹의 캡처를 대체하고 원래 문자열의 나머지 부분은 그대로 둡니다. 이 문제에 대해 궁금한 사람이 있으면 아래 해결 방법을 사용하게되었습니다. YMMV, 다른 솔루션 가능. 그의 의견과 옵션을 제공 한 Ansgar에게 많은 감사를드립니다.다음 코드 블록

:

  • $ 입력 교체
  • $ 정규식을 수행 될 한 줄의 텍스트가이 파일로부터 판독 (입력 여기서 문자열])는 정규식 지원되는 명명 된 그룹 중 하나 이상을 포함하는 것으로 확인 된 코드
  • $ regexToGroupName은 정규식 문자열을 [regex] :: GetGroupNames에서 반환 한 배열의 순서에 따라 그룹 이름 배열로 매핑하는 해시 테이블입니다()는 표현식에 표시되는 왼쪽에서 오른쪽 순서와 일치합니다.
  • $ groupNameToVersionNumber는 그룹 이름을 버전 번호에 매핑하는 해시 테이블입니다.

$ regex 내의 명명 된 그룹에 대한 제약 조건은 명명 된 그룹 내의 표현식을 중첩 할 수 없으며 입력 문자열 내에서 한 번만 일치해야합니다.

# This will give us the index and extent of each substring 
# that we will be replacing (the parts that we will not keep) 
$matchResults = ([regex]$regex).match($input) 

# This will hold substrings from $input that were not captured 
# by any of the supported named groups, as well as the replacement 
# version strings, properly ordered, but will omit substrings captured 
# by the named groups 
$lineParts = @() 
$startingIndex = 0 
foreach ($groupName in $regexToGroupName.$regex) 
{ 
    # Excise the substring leading up to the match for this group... 
    $lineParts = $lineParts + $input.Substring($startingIndex, $matchResults.groups[$groupName].Index - $startingIndex) 

    # Instead of the matched substring, we'll use the substitution 
    $lineParts = $lineParts + $groupNameToVersionNumber.$groupName 

    # Set the starting index of the next substring that we will keep... 
    $startingIndex = $matchResults.groups[$groupName].Index + $matchResults.groups[$groupName].Length 
} 

# Keep the end of the original string (if there's anything left) 
$lineParts = $lineParts + $input.Substring($startingIndex, $input.Length - $startingIndex) 

$newLine = "" 
foreach ($part in $lineParts) 
{ 
    $newLine = $newLine + $part 
} 
$input= $newLine 

답변

4

정규식은 그렇게 작동하지 않으므로 사용할 수 없습니다. 직접적인 것은 아닙니다.

$oldver = $input -replace $regexp, '$1,$2,$3,$4' 
$newver = $input -replace $oldver, "$Version1,$Version2,$Version3,$Version4" 
: 당신은 버전 문자열을 추출하고 두 번째 단계에서 새 버전 문자열이 문자열을 대체하는 것입니다 (그룹 당신이을 유지 에 원하는 부분이있는 더 적절한 정규 표현식을 사용하여 짧은) 할 수있는 일

편집 : 당신도 구조를 알 수없는 경우

, 당신은뿐만 아니라 정규 표현식에서 해당을 추출해야합니다.

$version = @($version1, $version2, $version3, $version4) 
$input -match $regexp 
$oldver = $regexp 
$newver = $regexp 
for ($i = 1; $i -le 4; $i++) { 
    $oldver = $oldver -replace "\(\?<version$i>\\d\)", $matches["version$i"] 
    $newver = $newver -replace "\(\?<version$i>\\d\)", $version[$i-1] 
} 
$input -replace $oldver, $newver 
+0

이것은 좋을 것이라고 동의하지만 사용자가 정규식과 파일 세트를 지정하는 유틸리티를위한 것입니다. 정규식을 알지 못하고 파일 내용이 어떤 모양인지 모르기 때문에 원본 파일 내용을 다시 포맷하지 않으면 첫 번째 줄을 사용할 수 없으므로 바람직하지 않습니다. 나중에 파일 내용을 동일하게 유지하면서 일치하는 행의 부분 문자열 만 개별 버전 필드로 대체해야합니다. – Hoobajoob

+0

아마도 정규식의 명명 된 그룹을 실제 이전/새 번호로 바꾼 다음 문자열 바꾸기를 수행 할 수 있습니다. 정규식에 명명 된 그룹 이외의 표현식이 포함되어 있으면 올바르게 작동하지 않습니다. –

+0

정규식의 명명 된 그룹이 실제로 정의 된 방법을 미리 알지 못하더라도 (예 : \ d, \ d {2}, \ d +, 리터럴 등을 찾을 수있는 등) . 명명 된 그룹 정의에 대한 몇 가지 제약 조건을 소개하고 정규식 구문에서 하나 이상의 문자와 영숫자를 허용하기 위해 위의 for 루프에서 사용 된 정규 표현식을 변경할 수 있습니다 (예 : 정규식에서 "\\ d"를 for 루프는 "[a-zA-Z0-9 \\ + \. \ * \? \^\ $ \ {\} \ | \ [\]] +"). 여하튼이 방법은 하위 문자열 작업보다 바람직합니다. – Hoobajoob

1

당신은 단순히 당신의 $input 텍스트 어딘가에서 발견 버전 번호를 교체하려는 시나리오에서는 간단한 솔루션

, 당신은 단순히이 작업을 수행 할 수 있습니다 :

$input -replace '(Version\s+)\d+,\d+,\d+,\d+',"`$1$Version1,$Version2,$Version3,$Version4" 

의 이름은 사용 PowerShell의 캡처

중괄호를 사용하여 명명 된 캡처에 대한 질문에 답하는 것입니다. 여러 캡처와

I have a pet dog. I have a pet cat. cher 

문제 당신은 대체 문자열 때문에, 같은 대체 성명에서 여러 값을 대체 할 수

& 솔루션 모두에 사용됩니다 : 즉

'dogcatcher' -replace '(?<pet>dog|cat)','I have a pet ${pet}. ' 

을 제공합니다 . 즉, 당신이 이런 짓을하는 경우 :

'dogcatcher' -replace '(?<pet>dog|cat)|(?<singer>cher)','I have a pet ${pet}. I like ${singer}''s songs. ' 

당신은 얻을 것 : 당신이 희망하는지 아마하지

I have a pet dog. I like 's songs. I have a pet cat. I like 's songs. I have a pet . I like cher's songs. 

을 ....

'dogcatcher' -replace '(?<pet>dog|cat)','I have a pet ${pet}. ' -replace '(?<singer>cher)', 'I like ${singer}''s songs. ' 

이 ... 얻을 :

오히려, 당신은 항목 당 일치를해야 할 것

I have a pet dog. I have a pet cat. I like cher's songs. 

더 복잡한 솔루션

이 다시 현실로 시나리오에서는 실제로 캡처 된 값을 사용하지 않습니다. 오히려 당신은 그들이 있던 공간을 새로운 가치로 바꾸기를 바라고 있습니다.

$input = 'I''m running Programmer''s Notepad version 2.4.2.1440, and am a big fan. I also have Chrome v 56.0.2924.87 (64-bit).' 

$version1 = 1 
$version2 = 3 
$version3 = 5 
$version4 = 7 

$v1Pattern = '(?<=\bv(?:ersion)?\s+)\d+(?=\.\d+\.\d+\.\d+)' 
$v2Pattern = '(?<=\bv(?:ersion)?\s+\d+\.)\d+(?=\.\d+\.\d+)' 
$v3Pattern = '(?<=\bv(?:ersion)?\s+\d+\.\d+\.)\d+(?=\.\d+)' 
$v4Pattern = '(?<=\bv(?:ersion)?\s+\d+\.\d+\.\d+\.)\d+' 

$input -replace $v1Pattern, $version1 -replace $v2Pattern, $version2 -replace $v3Pattern,$version3 -replace $v4Pattern,$version4 

줄 것이다 :

I'm running Programmer's Notepad version 1.3.5.7, and am a big fan. I also have Chrome v 1.3.5.7 (64-bit). 

NB :이를 위해, 당신은 단순히이 원하는 것 위의 1 라이너로 기록 될 수있다,하지만 나는 그것을 만들기 위해 그것을 분해 한 읽기가 더 쉽다.

정규 표현식 검색 방법을 사용합니다. 일치하는 문자열을 포함하지 않고 캡처중인 문자열 앞뒤에 내용을 확인하는 방법입니다. 즉 대체 할 항목을 선택할 때 "단어 버전 바꾸기"를 말하지 않고 "단어 버전 다음에 나타나는 번호와 일치"라고 말할 수 있습니다. 그 여기에

상세 정보 : http://www.regular-expressions.info/lookaround.html

귀하의 예

위의 적응은 예를 들어 작동하는 (즉, 버전은 쉼표 나 점으로 구분하고, 일관성이 자신의 형식이 없다 될 수있다 숫자 4 개 세트 인 이상 :

$input = @' 
#define SOME_MACRO(4, 1, 0, 0) 

Version "1.2.3.4" 

SomeStruct vs = { 99,99,99,99 } 
'@ 

$version1 = 1 
$version2 = 3 
$version3 = 5 
$version4 = 7 

$v1Pattern = '(?<=\b)\d+(?=\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\b)' 
$v2Pattern = '(?<=\b\d+\s*[\.,]\s*)\d+(?=\s*[\.,]\s*\d+\s*[\.,]\s*\d+\b)' 
$v3Pattern = '(?<=\b\d+\s*[\.,]\s*\d+\s*[\.,]\s*)\d+(?=\s*[\.,]\s*\d+\b)' 
$v4Pattern = '(?<=\b\d+\s*[\.,]\s*\d+\s*[\.,]\s*\d+\s*[\.,]\s*)\d+\b' 

$input -replace $v1Pattern, $version1 -replace $v2Pattern, $version2 -replace $v3Pattern,$version3 -replace $v4Pattern,$version4 

을 제공합니다 :

#define SOME_MACRO(1, 3, 5, 7) 

Version "1.3.5.7" 

SomeStruct vs = { 1,3,5,7 }