PowerShell, la formattazione di valori in un'altra cultura
-
24-09-2019 - |
Domanda
C'è un modo semplice in PowerShell per formattare i numeri e come in un altro locale?Attualmente sto scrivendo un paio di funzioni per facilitare SVG generazione per me e SVG usa .
come separatore decimale, mentre PowerShell onori la mia lingua impostazioni (de-DE
quando si converte i numeri in virgola mobile per le stringhe.
C'è un modo semplice per impostare un altro locale per una funzione o senza attaccare
.ToString((New-Object Globalization.CultureInfo ""))
dopo ogni double
variabile?
Nota:Questo è circa il locale usato per la formattazione, non la stringa di formato.
(Lato domanda:Devo usare la lingua inglese, in quel caso, o piuttosto en-US
?)
ETA: Beh, quello che sto cercando è qualcosa di simile al seguente:
function New-SvgWave([int]$HalfWaves, [double]$Amplitude, [switch]$Upwards) {
"<path d='M0,0q0.5,{0} 1,0{1}v1q-0.5,{2} -1,0{3}z'/>" -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 po ' di automazione per le cose tendo a scrivere tutto il tempo e il doppio valori di utilizzare il separatore decimale il punto invece della virgola (che uso nel mio locale).
ETA2: Interessante curiosità da aggiungere:
PS Home:> $d=1.23
PS Home:> $d
1,23
PS Home:> "$d"
1.23
Mettendo la variabile in una stringa il set locale non sembra applicare, in qualche modo.
Soluzione
Questo è un uso che funzione di PowerShell per testare lo script in altre culture. Credo che potrebbe essere utilizzato per quello che stai cercando:
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
Altri suggerimenti
Mentre Keith Hill risposta utile mostra come modificare uno script cultura corrente on demand (più moderna alternativa di PSv3+ e .NET framework v4.6+:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture
), c'è non c'è bisogno di cambiare la cultura, perché - come hai scoperto il tuo secondo aggiornamento per la questione - PowerShell stringa interpolazione (in alternativa all'uso del -f
operatore) utilizza sempre la invariante piuttosto che il corrente cultura:
In altre parole:
Se si sostituisce 'val: {0}' -f 1.2
con "val: $(1.2)"
, il valore letterale 1.2
è non formattato secondo le regole della cultura corrente.
È possibile verificare nella console di esecuzione (su una sola riga;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.
Sfondo:
Sorprendentemente, PowerShell sempre si applica la invariante piuttosto che il corrente cultura nel seguente stringa correlati contesti, se il tipo a portata di mano sostiene la cultura specifica di conversione da e per le stringhe:
Come spiegato in questa approfondita risposta, PowerShell esplicitamente richieste cultura-invariante elaborazione - passando l' [cultureinfo]::InvariantCulture
esempio - nei seguenti scenari:
Quando string-interpolazione:se il tipo di oggetto implementa l'
IFormattable
interfaccia;in caso contrario, PowerShell chiamate.psobject.ToString()
sull'oggetto.Quando casting:
- per una stringa, anche quando l'associazione a un
[string]
digitato il parametro:se il tipo di origine implementa l'[IFormattable]
interfaccia;in caso contrario, PowerShell chiamate.psobject.ToString()
. - da una stringa:se la destinazione di tipo statico
.Parse()
il metodo ha un sovraccarico con un[IFormatProvider]
digitato il parametro (che è un'interfaccia implementata da[cultureinfo]
).
- per una stringa, anche quando l'associazione a un
Quando string-confronto (
-eq
,-lt
,-gt
) , utilizzando ilString.Compare()
overload che accetta unCultureInfo
parametro.Gli altri?
Per quanto le impostazioni cultura invarianti è / è per:
La lingua inglese è lingua;è associato con la lingua inglese, ma non con qualsiasi paese/regione.
[...]
A differenza di cultura-dati sensibili, soggette a modifica da parte di personalizzazione da parte dell'utente o da aggiornamenti al .NET Quadro o il sistema operativo, impostazioni cultura invarianti dati stabile nel tempo e tra installato culture e non può essere personalizzato dagli utenti.Questo rende le impostazioni cultura invarianti particolarmente utile per le operazioni che richiedono cultura-risultati indipendenti, come ad esempio la formattazione e le operazioni di analisi, che continuano a persistere i dati formattati o l'ordinamento e l'ordinamento di operazioni che richiedono che i dati visualizzati in un ordine fisso a prescindere dalla cultura.
Presumibilmente, è la stabilità attraverso le culture che hanno motivato PowerShell progettisti di utilizzare sempre l'invariante di cultura quando implicitamente conversione da e stringhe.
Per esempio, se è rigido codice di una data stringa, ad esempio '7/21/2017'
in uno script e successivamente si tenta di convertire la data con un [date]
il cast, PowerShell cultura-invariante comportamento assicura che lo script non si rompe anche se per l'esecuzione, mentre una cultura diversa dall'inglese (USA è in vigore - fortunatamente, la lingua inglese riconosce anche ISO 8601-formato di data e ora stringhe;
ad esempio, [datetime] '2017-07-21'
lavora troppo.
Il rovescio della medaglia, se si desidera convertire da e per corrente-cultura-le stringhe appropriate, si deve fare così in modo esplicito.
Per riassumere:
La conversione per stringhe:
- Incorporamento di istanze di tipi di dati con sensibile alla cultura-by-default rappresentazioni di stringa all'interno
"..."
produce una culturainvariante rappresentazione ([double]
o[datetime]
sono esempi di questi tipi). - Per ottenere un corrente-cultura rappresentazione, chiamata
.ToString()
in modo esplicito o utilizzare-f
, la formattazione operatore (eventualmente all'interno"..."
tramite un allegando$(...)
).
- Incorporamento di istanze di tipi di dati con sensibile alla cultura-by-default rappresentazioni di stringa all'interno
La conversione da stringhe:
Diretta cast (
[<type>] ...
) sempre e solo riconosce la cultura-invariante rappresentazioni di stringa.Per la conversione da un corrente-cultura-adeguata rappresentazione in forma di stringa (o un specifiche la cultura della rappresentazione), utilizzare il tipo di target statico
::Parse()
metodo in modo esplicito (facoltativamente con un esplicito[cultureinfo]
istanza di rappresentare una cultura specifica).
Cultura-INVARIANTE esempi:
stringa di interpolazione e cast:
"$(1/10)"
e[string] 1/10
- sia resa letterale di stringa
0.1
, con separatore decimale.
, a prescindere dalla cultura corrente.
- sia resa letterale di stringa
Allo stesso modo, cast da stringhe sono cultura-invariante;ad esempio,
[double] '1.2'
.
è sempre riconosciuto come separatore decimale, a prescindere dalla cultura corrente.- Un altro modo di dirlo:
[double] 1.2
è non tradotto per la cultura-sensibiliper impostazione predefinita overload del metodo[double]::Parse('1.2')
, ma per la cultura-invariante[double]::Parse('1.2', [cultureinfo]::InvariantCulture)
per il confronto di stringhe (si supponga che
[cultureinfo]::CurrentCulture='tr-TR'
è, in effetti, - bagno turco, dovei
NON è una minuscola rappresentanza diI
)[string]::Equals('i', 'I', 'CurrentCultureIgnoreCase')
$false
con la cultura turca in vigore.'i'.ToUpper()
mostra che nella cultura turca il maiuscolo èİ
, nonI
.
'i' -eq 'I'
- è ancora
$true
, perché il invariante la cultura è applicato. - implicitamente lo stesso:
[string]::Equals('i', 'I', 'InvariantCultureIgnoreCase')
- è ancora
SENSIBILE alla cultura esempi:
La cultura attuale È rispettato nei seguenti casi:
Con
-f
, la stringa di formattazione operatore (come indicato sopra):[cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2
i rendimenti1,2
- Insidia:A causa di precedenza degli operatori, qualsiasi espressione come RHS della
-f
deve essere racchiusa in(...)
per essere riconosciuto come tale:- E. g.,
'{0}' -f 1/10
viene valutato come se('{0}' -f 1) / 10
era stato specificato;
utilizzare'{0}' -f (1/10)
invece.
- E. g.,
Di Default in uscita per la console:
ad esempio,
[cultureinfo]::CurrentCulture = 'de-DE'; 1.2
i rendimenti1,2
Lo stesso vale per l'output dei cmdlet;ad esempio,
[cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01'
i rendimenti
Sonntag, 1. Januar 2017 00:00:00
Caveat:Sembra che ci sia un bug come di Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.5:in alcuni scenari, valori letterali passato a un blocco di script come libera parametri può causare cultura-invariante di output di default - vedere questo problema GitHub
Durante la scrittura di un file con
Set-Content
/Add-Content
oOut-File
/>
/>>
:- ad esempio,
[cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt
i rendimenti1,2
- ad esempio,
Quando si utilizza la statica
::Parse()
/::TryParse()
metodi sul numero di tipi come[double]
mentre passa solo la stringa da analizzare;ad esempio, con la culturafr-FR
in effetti (dove,
è il separatore decimale),[double]::Parse('1,2')
restituisce il doppio1.2
(cioè,1 + 2/10
).- Caveat:Come bviktor sottolinea, di un separatore di migliaia riconosciuto per impostazione predefinita, ma in un modo molto sciolto moda:effettivamente, il separatore delle migliaia può essere collocato ovunque all'interno la parte intera, indipendentemente dal numero di cifre sono gruppi risultanti, e un leader
0
inoltre è accettato;ad esempio, nelen-US
cultura (dove,
è il separatore delle migliaia),[double]::Parse('0,18')
forse sorprendentemente riesce e rendimenti18
.- Per sopprimere il riconoscimento di un separatore di migliaia, usare qualcosa come
[double]::Parse('0,18', 'Float')
, viaNumberStyles
parametro
- Per sopprimere il riconoscimento di un separatore di migliaia, usare qualcosa come
- Caveat:Come bviktor sottolinea, di un separatore di migliaia riconosciuto per impostazione predefinita, ma in un modo molto sciolto moda:effettivamente, il separatore delle migliaia può essere collocato ovunque all'interno la parte intera, indipendentemente dal numero di cifre sono gruppi risultanti, e un leader
Involontaria la cultura di una sensibilità non essere corretti per mantenere la compatibilità all'indietro:
- Nel parametro vincolante per le conversioni di tipo i cmdlet (ma non per funzioni) - vedi questo problema GitHub.
- Nel
-as
operatore - vedere questo problema GitHub. - In
[hashtable]
ricerche chiave - vedere questa risposta e questo problema GitHub.
Gli altri?
Stavo pensando a come rendere più facile e si avvicinò con acceleratori:
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()
Spero che sarà di aiuto.
Se avete già la cultura caricato nel proprio ambiente,
#>Get-Culture
LCID Name DisplayName
---- ---- -----------
1031 de-DE German (Germany)
#>Get-UICulture
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
è possibile risolvere il problema:
PS Home:> $d=1.23
PS Home:> $d
1,23
in questo modo:
$d.ToString([cultureinfo]::CurrentUICulture)
1.23
Naturalmente è necessario tenere a mente che se gli altri utenti di eseguire lo script con un'impostazione locale diversa, i risultati potrebbero non risultare come originariamente previsto.
Tuttavia, questa soluzione potrebbe tornare utile. Buon divertimento!