Saltar al contenido

PowerShell, formateo de valores en otra cultura

Nuestro grupo de especialistas despúes de días de investigación y de recopilar de datos, obtuvimos la respuesta, deseamos que todo este artículo sea de utilidad en tu trabajo.

Solución:

Si bien la útil respuesta de Keith Hill le muestra cómo cambiar la cultura actual de un script a pedido (alternativa más moderna a partir de PSv3 + y .NET Framework v4.6 +:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture), hay no hay necesidad de cambiar la cultura, porque, como descubrió en la segunda actualización de la pregunta, De PowerShell string interpolación (en lugar de usar el -f operador) siempre utiliza el invariante en lugar del Actual cultura:

En otras palabras:

Si reemplaza 'val: 0' -f 1.2 con "val: $(1.2)", el literal 1.2 es no formateado 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:

Asombrosamente, Potencia Shell siempre aplica el invariante en lugar del Actual cultura en el siguiente string-relacionado contextos, si el tipo en cuestión admite la conversión específica de la cultura hacia y desde cadenas:

Como se explica en esta respuesta detallada, PowerShell solicita explícitamente invariante de cultura Procesando – pasando el [cultureinfo]::InvariantCulture instancia – en los siguientes escenarios:

  • Cuando string-interpolando: si el tipo del objeto implementa el IFormattable interfaz; de lo contrario, PowerShell llama .psobject.ToString() en el objeto.

  • Cuando fundición:

    • para a string, incluso cuando vinculante a un [string]-parámetro de tipo: si el tipo de fuente implementa el [IFormattable] interfaz; de lo contrario, PowerShell llama .psobject.ToString().
    • de a string: si el tipo de destino static .Parse() El método tiene una sobrecarga con un [IFormatProvider]-parámetro de tipo (que es una interfaz implementada por [cultureinfo]).
  • Cuando string-comparar (-eq, -lt, -gt) , utilizando el String.Compare() sobrecarga que acepta un CultureInfo parámetro.

  • ¿Otros?

En cuanto a para qué 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 culturales invariables son estable a lo largo del tiempo y en todas las culturas instaladas y los usuarios no pueden personalizarlo. Esto hace que la cultura invariante sea particularmente útil para operaciones que requieren resultados independientes de la cultura, como operaciones de formato y análisis que conservan datos formateados, u operaciones de clasificación y orden que requieren que los datos se muestren en un orden fijo independientemente de la cultura.

Presumiblemente, es la estabilidad entre culturas lo que motivó a los diseñadores de PowerShell a usar consistentemente la cultura invariante cuando implícitamente convertir hacia y desde cadenas.

Por ejemplo, si codifica una fecha string tal como '7/21/2017' en un script y luego intente convertirlo a la fecha con un [date] cast, el comportamiento invariante de la cultura de PowerShell garantiza que el script no se rompa incluso cuando se ejecuta mientras está en vigor una cultura que no sea el inglés de EE. UU. afortunadamente, la cultura invariante también reconoce cadenas de fecha y hora en formato ISO 8601;
p.ej, [datetime] '2017-07-21' también funciona.

Por otro lado, si quieres convertir ay desde Actual-cadenas apropiadas para la cultura, debes hacerlo explícitamente.

Para resumir:

  • Mudado para instrumentos de cuerda:

    • Incrustación de instancias de tipos de datos con valores culturales de forma predeterminada string representaciones en el interior "..." produce una culturainvariante representación[double] o [datetime] son ejemplos de tales tipos).
    • Conseguir un Actual-representación cultural, convocatoria .ToString() explícitamente o usar -f, el operador de formato (posiblemente dentro "..." a través de un cerramiento $(...)).
  • Mudado de instrumentos de cuerda:

    • Un elenco directo ([] ...) solo reconoce la cultura-invariante string representaciones.

    • Para convertir de un Actualapropiado para la cultura string representación (o una específico la representación de la cultura), utilice el tipo de destino static ::Parse() método explícitamente (opcionalmente con un explícito [cultureinfo] instancia para representar una cultura específica).


Ejemplos de cultura INVARIANTE:

  • string interpolación y yesos:

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

      • ambos rinden string literal 0.1, con marca decimal ., independientemente de la cultura actual.
    • Similar, yesos de instrumentos de cuerda son invariantes con respecto a la cultura; p.ej, [double] '1.2'

      • . es siempre reconocido como la marca decimal, independientemente de la cultura actual.
      • Otra forma de decirlo: [double] 1.2 es no traducido a la cultura-sensible-sobrecarga de método por defecto [double]::Parse('1.2'), pero a la cultura-invariante[double]::Parse('1.2', [cultureinfo]::InvariantCulture)
  • string comparación (asumir que [cultureinfo]::CurrentCulture='tr-TR' está en efecto – turco, donde i NO es una representación en minúsculas de I)

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

Ejemplos sensibles a la cultura:

Se respeta la cultura actual en los siguientes casos:

  • Con -f, los string-operador de formato (como se señaló anteriormente):

    • [cultureinfo]::currentculture = 'de-DE'; '0' -f 1.2 rendimientos 1,2
    • Escollo: debido a la precedencia del operador, cualquier expresión como el RHS de -f debe estar encerrado en (...) para ser reconocido como tal:
      • P.ej, '0' -f 1/10 se evalúa como si ('0' -f 1) / 10 había sido especificado;
        usar '0' -f (1/10) en lugar de.
  • Defecto salida a la consola:

    • p.ej, [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' rendimientos
      Sonntag, 1. Januar 2017 00:00:00

    • Consideración: Parece haber un insecto 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 que no varía según la cultura; consulte este problema de GitHub

  • Al escribir a un expediente con Set-Content/Add-Content o Out-File / > / >>:

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

    • Consideración: Como señala bviktor, los separadores de miles se reconocen de forma predeterminada, pero de una manera muy flexible: efectivamente, el separador de miles se puede colocar en cualquier sitio dentro de la porción entera, independientemente de cuántos dígitos haya en los grupos resultantes, y un 0 también se acepta; por ejemplo, en el en-US cultura (donde , es el separador de miles), [double]::Parse('0,18') quizás sorprendentemente triunfa y rinde 18.
      • Para suprimir el reconocimiento de separadores de miles, use algo como [double]::Parse('0,18', 'Float'), mediante el NumberStyles parámetro
  • Involuntario sensibilidad cultural que no se corregirá para preservar la compatibilidad con versiones anteriores:

    • En conversiones de tipo de enlace de parámetros para compilado cmdlets (pero Código de PowerShell – scripts o funciones – es cultura invariante): consulte este problema de GitHub.
    • En el -as operador – ver este problema de GitHub.
    • En [hashtable] key búsquedas – vea esta respuesta y este problema de GitHub.
    • [Decision pending] En el LHS de -replace operaciones – vea este problema de GitHub.
  • ¿Otros?

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

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

Estaba pensando en cómo hacerlo más fácil y se me ocurrieron 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 te ayude.

Aquí puedes ver las reseñas y valoraciones de los lectores

Si te mola la invitación, tienes la opción de dejar un artículo acerca de qué te ha impresionado de este enunciado.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *