Digamos que tengo una expresión regular como la siguiente, pero la cargué de un archivo en una variable $ regex, por lo que no tengo idea de su contenido en tiempo de diseño, pero en tiempo de ejecución puedo descubrir que incluye el "version1", "versión 2", "versión 3" y "version4" grupos nombrados:Powershell: Reemplazando los grupos denominados expresiones regulares con variables
"Version (?<version1>\d),(?<version2>\d),(?<version3>\d),(?<version4>\d)"
... y tengo estas variables:
$version1 = "3"
$version2 = "2"
$version3 = "1"
$version4 = "0"
.. .y me encuentro con la siguiente cadena en un archivo:
Version 7,7,0,0
... que se almacena en una entrada $ variable, de modo que ($ input -match $ regex) evalúa a $ true.
¿Cómo puedo reemplazar los grupos nombrados de $ regex en la cadena $ input con los valores de $ version1, $ version2, $ version3, $ version4 si no sé el orden en que aparecen en $ regex (I solo se sabe que $ regex incluye estos grupos nombrados)?
No encuentro referencias que describan la sintaxis para reemplazar un grupo con nombre por el valor de una variable utilizando el nombre del grupo como índice de la coincidencia. ¿Esto es compatible?
EDIT: Para aclarar - el objetivo es reemplazar cadenas de versión con plantilla en cualquier tipo de archivo de texto en el que la cadena de versión de un archivo dado requiere el reemplazo de un número variable de campos de versión (pueden ser 2, 3, o los 4 campos). Por ejemplo, el texto en un archivo podría parecerse a cualquiera de estos (pero no está limitado a estos):
#define SOME_MACRO(4, 1, 0, 0)
Version "1.2.3.4"
SomeStruct vs = { 99,99,99,99 }
Los usuarios pueden especificar un conjunto de archivos y una expresión regular para que coincida con la línea que contiene los campos, con el la idea original es que los campos individuales serían capturados por grupos nombrados. La utilidad tiene los valores de campo de versión individuales que deben sustituirse en el archivo, pero debe conservar el formato original de la línea que contendrá las sustituciones y sustituir solo los campos solicitados.
EDITAR-2: creo que puedo conseguir el resultado que necesito con cálculos subcadena en base a la posición y el alcance de cada uno de los partidos, pero tenía la esperanza operación de sustitución de Powershell me iba a ahorrar algo de trabajo.
EDITAR-3: Así que, como Ansgar describe correctamente y sucintamente a continuación, no hay una manera (usando sólo la cadena de entrada original, una expresión regular de la que sólo conoce los grupos nombrados, y la consiguiente coincidencias) para utilizar la operación "-replace" (u otras operaciones de expresiones regulares) para realizar sustituciones de las capturas de los grupos nombrados, mientras se deja intacto el resto de la cadena original. Para este problema, si alguien tiene curiosidad, terminé usando la solución a continuación. YMMV, otras soluciones posibles. Muchas gracias a Ansgar por sus comentarios y opciones.
En el siguiente bloque de código:
- $ de entrada es una línea de texto en el que la sustitución se va a realizar
- $ expresiones regulares es una expresión regular (de tipo [cadena]) leer desde un archivo que se ha verificado que contiene al menos uno de los grupos con nombre admitidos
- $ regexToGroupName es una tabla hash que asigna una cadena regex a una matriz de nombres de grupos ordenados de acuerdo con el orden de la matriz devuelta por [regex] :: GetGroupNames(), que coincide con el orden de izquierda a derecha en el que aparecen en la expresión
- $ groupNameToVersionNumber es una tabla hash que asigna un nombre de grupo a un número de versión.
Restricciones en los grupos nombrados dentro de $ regex son solo (creo) que la expresión dentro de los grupos nombrados no se puede anidar, y debe coincidir como máximo una vez dentro de la cadena de entrada.
# 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
De acuerdo en que esto sería bueno, pero esto es para una utilidad donde los usuarios especifican una expresión regular y un conjunto de archivos. No conozco la expresión regular, y no sé cómo se ve el contenido del archivo, por lo que no pude usar la primera línea en su respuesta sin reformatear el contenido original del archivo, lo que sería indeseable. Debo dejar el contenido del archivo con el mismo aspecto luego, reemplazando solo las subcadenas en las líneas correspondientes con los campos de versión individuales. – Hoobajoob
Quizás pueda reemplazar los grupos nombrados en la expresión regular con los números antiguos/nuevos reales y luego hacer una cadena reemplazar. Sin embargo, eso no funcionará correctamente si la expresión regular contiene expresiones distintas de los grupos nombrados. –
Esto casi funciona, aunque no sé de antemano cómo se definen realmente los grupos nombrados en la expresión regular (por ejemplo, podrían estar buscando \ d, \ d {2}, \ d +, un literal, etc.) . Puedo introducir algunas restricciones en la definición del grupo nombrado y cambiar la expresión regular utilizada en el ciclo for que tiene arriba para admitir uno o más caracteres de la sintaxis de expresiones regulares así como alfanuméricos (por ejemplo, reemplace "\\ d" en la expresión regular dentro de los bucles for con "[a-zA-Z0-9 \\ + \. \ * \? \^\ $ \ {\} \ | \ [\]] +"). En cualquier caso, este enfoque es preferible a las operaciones de subcadenas. – Hoobajoob