PowerShell, formateando valores en otra cultura

¿Hay alguna manera fácil en PowerShell para formatear números y similares en otra ubicación? Actualmente estoy escribiendo algunas funciones para facilitar la generación de SVG para mí y SVG . como un separador decimal, mientras que PowerShell respeta mis configuraciones locales ( de-DE ) al convertir números de coma flotante en cadenas.

¿Hay una forma fácil de configurar otra locale para una función o así sin excel?

 .ToString((New-Object Globalization.CultureInfo "")) 

después de cada double variable?

Nota: Se trata de la configuración regional utilizada para formatear, no de la cadena de formato.

(Pregunta complementaria: ¿Debería usar la cultura invariante en ese caso o más bien en-US ?)

ETA: Bueno, lo que bash aquí es algo como lo siguiente:

 function New-SvgWave([int]$HalfWaves, [double]$Amplitude, [switch]$Upwards) { "" -f ( $(if ($Upwards) {-$Amplitude} else {$Amplitude}), ("t1,0" * ($HalfWaves - 1)), $(if ($Upwards -xor ($HalfWaves % 2 -eq 0)) {-$Amplitude} else {$Amplitude}), ("t-1,0" * ($HalfWaves - 1)) ) } 

Solo un poco de automatización para cosas que tiendo a escribir todo el tiempo y los valores dobles necesitan usar el punto decimal en lugar de una coma (que usan en mi localidad).

ETA2: curiosidades interesantes para agregar:

 PS Home:> $d=1.23 PS Home:> $d 1,23 PS Home:> "$d" 1.23 

Al poner la variable en una cadena, la configuración regional no parece aplicarse, de alguna manera.

Esta es una función de PowerShell que uso para probar script en otras culturas. Creo que podría usarse para lo que busca:

 function Using-Culture ([System.Globalization.CultureInfo]$culture =(throw "USAGE: Using-Culture -Culture culture -Script {scriptblock}"), [ScriptBlock]$script=(throw "USAGE: Using-Culture -Culture culture -Script {scriptblock}")) { $OldCulture = [System.Threading.Thread]::CurrentThread.CurrentCulture $OldUICulture = [System.Threading.Thread]::CurrentThread.CurrentUICulture try { [System.Threading.Thread]::CurrentThread.CurrentCulture = $culture [System.Threading.Thread]::CurrentThread.CurrentUICulture = $culture Invoke-Command $script } finally { [System.Threading.Thread]::CurrentThread.CurrentCulture = $OldCulture [System.Threading.Thread]::CurrentThread.CurrentUICulture = $OldUICulture } } PS> $res = Using-Culture fr-FR { 1.1 } PS> $res 1.1 

Mientras que la útil respuesta de Keith Hill muestra cómo cambiar la cultura actual de un guión bajo demanda (una alternativa más moderna a partir de PSv3 + y .NET framework v4.6 +:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture ), no hay necesidad de cambiar la cultura , porque, como ha descubierto en su segunda actualización de la pregunta, la interpolación de cadenas de PowerShell (en lugar de usar la -f operador) siempre usa el invariante en lugar de la cultura actual :

En otras palabras:

Si reemplaza 'val: {0}' -f 1.2 con "val: $(1.2)" , el literal 1.2 no se formateará de acuerdo con las reglas de la cultura actual.
Puede verificar en la consola ejecutando ( en una sola línea : PSv3 +, .NET framework v4.6 +):

  PS> [cultureinfo]::currentculture = 'de-DE'; 'val: {0}' -f 1.2; "val: $(1.2)" val: 1,2 # -f operator: GERMAN culture applies, where ',' is the decimal mark val: 1.2 # string interpolation: INVARIANT culture applies, where '.' is the decimal mark. 

Fondo:

Sorprendentemente, PowerShell siempre aplica el invariante en lugar de la cultura actual en los siguientes contextos relacionados con cadenas , si el tipo en cuestión admite la conversión específica de la cultura hacia y desde cadenas:

Tal como se explica en esta respuesta detallada , PowerShell solicita explícitamente el proceso invariante de cultivo , al pasar la [cultureinfo]::InvariantCulture – en los siguientes escenarios:

  • Al interpolar cadenas : si el tipo del objeto implementa la interfaz IFormattable ; de lo contrario, PowerShell llama a .psobject.ToString() en el objeto.

  • Cuando lanzas :

    • a una cadena , incluso cuando se vincula a un parámetro de tipo [string] : si el tipo de fuente implementa la interfaz [IFormattable] ; de lo contrario, PowerShell llama a .psobject.ToString() .
    • desde una cadena : si el método static .Parse() del tipo de .Parse() tiene una sobrecarga con un parámetro de tipo [IFormatProvider] (que es una interfaz implementada por [cultureinfo] ).
  • Cuando se comparan -eq ( -eq , -lt , -gt ), usando la sobrecarga String.Compare() que acepta un parámetro CultureInfo .

  • ¿Otros?

En cuanto a lo que es / es la cultura invariante :

La cultura invariante es insensible a la cultura; está asociado con el idioma inglés, pero no con ningún país / región .
[…]
A diferencia de los datos sensibles a la cultura, que están sujetos a cambios por personalización del usuario o por actualizaciones de .NET Framework o el sistema operativo, los datos de cultivo invariables son estables a lo largo del tiempo y en culturas instaladas y los usuarios no pueden personalizarlos. Esto hace que la cultura invariante sea particularmente útil para operaciones que requieren resultados independientes de la cultura, como operaciones de formateo y análisis que persisten datos formateados u operaciones de clasificación y ordenamiento que requieren que los datos se muestren en un orden fijo independientemente de la cultura.

Presumiblemente, es la estabilidad en todas las culturas lo que motivó a los diseñadores de PowerShell a usar consistentemente la cultura invariante al convertir implícitamente cadenas a cadenas .

Por ejemplo, si codifica una secuencia de fecha como '7/21/2017' en una secuencia de comandos y luego intenta convertirla a la fecha con un molde de [date] , el comportamiento invariante de cultura de PowerShell asegura que la secuencia de comandos no lo haga punto de equilibrio cuando se ejecuta mientras una cultura que no sea EE. UU.-inglés está en vigencia; afortunadamente, la cultura invariante también reconoce las cadenas de fecha y hora del formato ISO 8601 ;
por ejemplo, [datetime] '2017-07-21' también funciona.

Por otro lado, si desea convertir ay de cadenas apropiadas para el cultivo actual , debe hacerlo explícitamente .

Para resumir:

  • Convirtiendo a cadenas :

    • Incrustar instancias de tipos de datos con representaciones de cadenas sensibles al cultivo por defecto dentro de "..." produce una representación invariante de cultivo ( [double] o [datetime] son ejemplos de tales tipos).
    • Para obtener una representación cultural actual , llame a .ToString() explícitamente o use -f , el operador de formateo (posiblemente dentro de "..." través de un $(...) ) adjunto.
  • Conversión de cadenas :

    • Un lanzamiento directo ( [] ... ) solo reconoce representaciones de cadenas invariantes en la cultura.

    • Para convertir desde una representación de cadena apropiada de cultivo actual (o una representación de una cultura específica ), use explícitamente el método static ::Parse() del tipo de destino (opcionalmente con una instancia explícita [cultureinfo] para representar una cultura específica).


Ejemplos de Cultura-INVARIANTE:

  • interpolación de cuerdas y moldes :

    • "$(1/10)" y [string] 1/10

      • ambos producen cadena literal 0.1 , con marca decimal . , independientemente de la cultura actual.
    • De manera similar, los moldes de cuerdas son invariantes para la cultura; por ejemplo, [double] '1.2'

      • . siempre se reconoce como la marca decimal, independientemente de la cultura actual.
      • Otra forma de expresslo: [double] 1.2 no se traduce en la sobrecarga del método dependiente de cultura-por-defecto [double]::Parse('1.2') , sino en el invariante de cultura [double]::Parse('1.2', [cultureinfo]::InvariantCulture)
  • comparación de cadenas (suponiendo que [cultureinfo]::CurrentCulture='tr-TR' está en efecto – en turco, donde i NO es una representación en minúscula de I )

    • [string]::Equals('i', 'I', 'CurrentCultureIgnoreCase')
      • $false con la cultura turca vigente.
      • 'i'.ToUpper() muestra que en la cultura turca, la mayúscula es İ , no I .
    • 'i' -eq 'I'
      • sigue siendo $true , porque se aplica la cultura invariante .
      • implícitamente lo mismo que: [string]::Equals('i', 'I', 'InvariantCultureIgnoreCase')

Ejemplos de cultura-SENSIBILIDAD:

La cultura actual se respeta en los siguientes casos:

  • Con -f , el operador de formateo de cadenas (como se indicó anteriormente):

    • [cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2 [cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2 rinde 1,2
    • Peligro: debido a la precedencia del operador , cualquier expresión como RHS de -f debe estar encerrada en (...) para ser reconocida como tal:
      • Por ejemplo, '{0}' -f 1/10 se evalúa como si ('{0}' -f 1) / 10 hubiera sido especificado;
        use '{0}' -f (1/10) lugar.
  • Salida predeterminada a la consola :

    • por ejemplo, [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 rendimientos 1,2

    • Lo mismo se aplica a la salida de cmdlets; p.ej,
      [cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01' [cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01' rendimientos de [cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01'
      Sonntag, 1. Januar 2017 00:00:00

    • Advertencia : parece haber un error a partir de Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.5: en ciertos escenarios, los literales pasados ​​a un bloque de script como parámetros no restringidos pueden dar como resultado una salida predeterminada invariante de cultivo – vea esto Problema de GitHub

  • Al escribir en un archivo con Set-Content / Add-Content o Out-File / > / >> :

    • por ejemplo, [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt produce 1,2
  • Al usar los métodos static ::Parse() / ::TryParse() en tipos de números como [double] mientras se pasa solo la cadena para analizar; por ejemplo, con cultura fr-FR vigente (donde , es la marca decimal), [double]::Parse('1,2') devuelve doble 1.2 (es decir, 1 + 2/10 ).

    • Advertencia : como señala bviktor , miles de separadores se reconocen por defecto, pero de una manera muy flexible: de hecho, el separador de miles se puede colocar en cualquier lugar dentro de la parte entera, independientemente de cuántos dígitos hay en los grupos resultantes, y un 0 líder también es aceptado; por ejemplo, en la cultura en-US (donde , es el separador de miles), [double]::Parse('0,18') quizás sorprendentemente tiene éxito y rinde 18 .
      • Para suprimir el reconocimiento de separadores de miles, use algo como [double]::Parse('0,18', 'Float') , a través del parámetro NumberStyles
  • ¿Otros?

Estaba pensando en cómo hacerlo más fácil y se me ocurrió aceleradores:

 Add-type -typedef @" using System; public class InvFloat { double _f = 0; private InvFloat (double f) { _f = f; } private InvFloat(string f) { _f = Double.Parse(f, System.Globalization.CultureInfo.InvariantCulture); } public static implicit operator InvFloat (double f) { return new InvFloat(f); } public static implicit operator double(InvFloat f) { return f._f; } public static explicit operator InvFloat (string f) { return new InvFloat (f); } public override string ToString() { return _f.ToString(System.Globalization.CultureInfo.InvariantCulture); } } "@ $acce = [type]::gettype("System.Management.Automation.TypeAccelerators") $acce::Add('f', [InvFloat]) $y = 1.5.ToString() $z = ([f]1.5).ToString() 

Espero que ayude

Si ya tiene la cultura cargada en su entorno,

  #>Get-Culture LCID Name DisplayName ---- ---- ----------- 1031 de-DE German (Germany) #>Get-UICulture LCID Name DisplayName ---- ---- ----------- 1033 en-US English (United States) 

es posible resolver este problema:

 PS Home:> $d=1.23 PS Home:> $d 1,23 

Me gusta esto:

 $d.ToString([cultureinfo]::CurrentUICulture) 1.23 

Por supuesto, debe tener en cuenta que si otros usuarios ejecutan el script con una configuración regional diferente, es posible que los resultados no se obtengan como se pretendía originalmente.

Sin embargo, esta solución podría ser útil. ¡Que te diviertas!