Powiedzmy, że mam wyrażenie regularne podobne do poniższego, ale wczytałem je z pliku do zmiennej $ regex, więc nie mam pojęcia w czasie projektowania, jaka jest jej zawartość, ale w czasie wykonywania mogę dowiedzieć się, że zawiera ona "version1", "Version2", "version3" i "version4" o nazwie grupy:Powershell: Zastępowanie regex nazwanych grup zmiennymi
"Version (?<version1>\d),(?<version2>\d),(?<version3>\d),(?<version4>\d)"
... i mam te zmienne:
$version1 = "3"
$version2 = "2"
$version3 = "1"
$version4 = "0"
.. .i natrafiam na następujący ciąg w pliku:
Version 7,7,0,0
... która jest przechowywana w zmiennej $ input, dzięki czemu ($ input -match $ regex) jest wartością $ true.
Jak mogę zamienić nazwy grup od $ regex w ciągu znaków $ input z wartościami $ wersja1, $ wersja2, $ wersja3, $ wersja4, jeśli nie znam kolejności, w jakiej pojawiają się w $ regex (I wiesz tylko, że $ regex zawiera te nazwane grupy)?
Nie mogę znaleźć żadnych odniesień opisujących składnię zastępowania nazwanej grupy wartością zmiennej, używając nazwy grupy jako indeksu do dopasowania - czy to jest nawet obsługiwane?
EDIT: Dla wyjaśnienia - celem jest zastąpienie matrycy ciągów Wersja w każdym rodzaju pliku tekstowego, gdzie ciąg wersja danego pliku wymaga wymiany zmienną liczbę pól wersja (może być 2, 3, lub wszystkie 4 pola). Na przykład tekst w pliku może wyglądać jak każdy z nich (ale nie ogranicza się do nich):
#define SOME_MACRO(4, 1, 0, 0)
Version "1.2.3.4"
SomeStruct vs = { 99,99,99,99 }
Użytkownicy mogą określić zestaw plików i wyrażenia regularnego, aby dopasować linię zawierającą pola, z Pierwotny pomysł polegał na tym, że poszczególne pola zostały przechwycone przez nazwane grupy. Narzędzie ma indywidualne wartości pól wersji, które powinny zostać zastąpione w pliku, ale musi zachować oryginalny format wiersza, który będzie zawierał podstawienia, i zastąpić tylko żądane pola.
EDIT-2: myślę, że mogę uzyskać wynik muszę z obliczeń podciągu na podstawie pozycji i zakresu każdego z meczów, ale miał nadzieję zastąpić działanie PowerShell został uratuje mi jakąś pracę.
EDIT-3: Tak, jak Ansgar poprawnie i zwięźle opisuje poniżej, nie jest sposobem (tylko przy użyciu oryginalnego ciąg wejściowy, wyrażenie regularne, o których znasz tylko nazwanych grup, a otrzymaną mecze), aby użyć operacji "-replace" (lub innych operacji wyrażeń regularnych) w celu dokonania podstawienia przechwyceń nazwanych grup, pozostawiając resztę oryginalnego ciągu nietkniętymi. Dla tego problemu, jeśli ktoś jest ciekawy, w końcu skorzystałem z poniższego rozwiązania. YMMV, możliwe inne rozwiązania. Wielkie dzięki dla Ansgara za jego opinie i opcje.
W kolejnym bloku kodu:
- $ wejściowy jest linia tekstu, w którym podstawienie ma być wykonywana
- $ regex jest wyrażeniem regularnym (typu [łańcuch]) odczytać z pliku który został zweryfikowany i zawiera co najmniej jedną z obsługiwanych nazwanych grup.
- $ regexToGroupName jest tablicą asocjacyjną, która odwzorowuje ciąg regex na tablicę nazw grup uporządkowanych zgodnie z kolejnością tablic zwracanych przez [regex] :: GetGroupNames(), które pasuje do kolejności od lewej do prawej, w której występują w wyrażeniu
- $ groupNameToVersionNumber to tablica skrótów, która odwzorowuje nazwę grupy na numer wersji.
Ograniczenia wymienionych grup w $ regex są tylko (chyba), że wyrażenie w obrębie wymienionych grup nie mogą być zagnieżdżone, i powinna odpowiadać co najwyżej raz w ciągu wejściowego.
# 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
Uzgodniono, że byłoby to miłe, ale dotyczy to narzędzia, w którym użytkownicy określają wyrażenie regularne i zbiór plików. Nie znam wyrażenia regularnego i nie wiem, jak wygląda zawartość pliku, więc nie mogłem użyć pierwszej linii w odpowiedzi bez ponownego formatowania oryginalnej zawartości pliku, co byłoby niepożądane. Musimy pozostawić zawartość pliku wyglądającą tak samo później, zastępując tylko podciągi pasujących wierszy polami poszczególnych wersji. – Hoobajoob
Być może możesz zamienić nazwane grupy w wyrażeniu regularnym na rzeczywiste stare/nowe liczby, a następnie zastąp ciąg. To nie będzie działać poprawnie, jeśli wyrażenie regularne zawiera wyrażenia inne niż nazwane grupy. –
To prawie działa, chociaż nie wiem z góry, jak właściwie określone grupy w regex są zdefiniowane (np. Mogą szukać \ d, \ d {2}, \ d +, literału itp.) . Mogę wprowadzić pewne ograniczenia dla nazwanej definicji grupy i zmienić wyrażenie użyte w pętli for, którą posiadasz powyżej, aby przyznać jeden lub więcej znaków ze składni regex oraz alfanumerycznych (np. Zamień "\\ d" w regex ciągu pętle for z "[a-zA-Z0-9 \\ + \. \ * \? \^\ $ \ {\} \ | \ [\]] +"). W każdym razie podejście to jest lepsze niż działanie podłańcuchowe. – Hoobajoob