2012-09-01 44 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に格納されています。

文字列$ inputの$ regexの名前付きグループを、$ regex($ 1、$ 2、$ version3、$ version4)の値で置き換えることはできますか? $ regexにはこれらの名前付きグループが含まれていることだけを知っていますか?)

グループ名を一致のインデックスとして使用して、名前付きグループを変数の値に置き換える構文についての説明はありません。これはこれでもサポートされていますか?

EDIT: を明確にする - 目標は、指定したファイル内のバージョン文字列は、バージョンフィールドの可変数の交換が必要なテキストファイルの任意の種類でテンプレートのバージョン文字列を置き換えることである(2、3可能性があり、または4つすべてのフィールド)。たとえば、ファイル内のテキストは、これらのいずれかのように見えることができます(ただし、これらに限定されない):で

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

Version "1.2.3.4" 

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

ユーザーは、ファイルセットとフィールドを含む行にマッチする正規表現を指定することができます元のアイデアは、個々のフィールドが名前付きグループによってキャプチャされるということです。ユーティリティには、ファイル内で置換する必要がある個々のバージョンフィールド値がありますが、置換を含む行の元の形式を保持し、要求されたフィールドのみを置換する必要があります。

EDIT-2: は私が、私は試合のそれぞれの位置や程度に基づいて、サブストリングの計算に必要な結果を得ることができると思うが、PowerShellのの置換操作は、私にいくつかの作業を保存するつもりだった期待していました。

EDIT-3:正しくかつ簡潔に以下の記述Ansgarのよう ので、そこだけ元の入力文字列、あなただけの名前のグループを知っているかについて、正規表現を使用した方法は(ではない、そして得られました"-replace"演算(または他の正規表現演算)を使用して、指定されたグループのキャプチャの置換を実行し、残りの残りの文字列はそのまま残します。この問題のために、誰かが好奇心が強い場合は、以下の解決策を使用して終了しました。 YMMV、他の解決策が可能です。彼のフィードバックとオプションを提供してくれたAnsgarに感謝します。次のコードブロック内

  • $入力が行われる
  • $正規表現は、(タイプの[文字列])正規表現である置換にテキストの行では、ファイルから読み込まサポートされている名前付きグループの少なくとも1つを含むことが検証されている
  • $ regexToGroupNameは、正規表現文字列を、[regex] :: GetGroupNamesによって返される配列の順序に従ってグループ名の配列にマッピングするハッシュテーブルです()は、式に表示される左から右の順番に一致します
  • $ groupNameToVersionNumberは、グループ名をバージョン番号にマップするハッシュテーブルです。

$ regex内の名前付きグループの制約は、名前付きグループ内の式をネストすることができず、入力文字列内で最大で1回一致する必要があります(私は思う)。

# 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" 
:あなたは、バージョン文字列を抽出した後、第2のステップで新しいバージョン文字列とその部分文字列を置き換えることです(グループあなたはを維持 したい部分があることがより適切な正規表現を使用しての短い)何ができますか

編集:

あなたも構造がわからない場合、あなたにも正規表現からそれを抽出する必要があります。あなたは、単にあなたの$inputテキストのどこかで見つかったバージョン番号を交換したいシナリオで

$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ループで使用される正規表現を、正規表現の構文と英数字から1つ以上の文字を許可するように変更することができます(例えば、正規表現内の "\\ d" forループは "[a-zA-Z0-9 \\ + \。\ * \?\ \ \ \ \ \ \ \ \ \] \")。いずれにせよ、この方法は部分文字列操作よりも好ましい方法です。 – Hoobajoob

1

シンプルなソリューション

、あなたは単にこれを行うことができます:名前を使用して

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

をPowerShellでのキャプチャ

Reあなたの名前付きキャプチャについての質問には、中括弧を使って答えることができます。すなわち

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

与える:

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

複数のキャプチャで問題&ソリューション

を置換文字列はすべてのために使用されているので、あなたは、同じ置き換える文で複数の値を置き換えることはできません。すなわち、あなたはこれをしなかった場合:

'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 } 
関連する問題