PowerShell, formatting values โ€‹โ€‹in a different culture

Is there an easy way in PowerShell to format numbers, etc. in another locale? I am currently writing several functions to simplify the generation of SVG for me, and SVG uses . as a decimal separator, while PowerShell evaluates my locale ( de-DE ) settings when converting floating-point numbers to strings.

Is there an easy way to set a different locale for a function or without it without sticking

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

after each double variable?

Note. This applies to the language used for formatting, not for the format string.

(Side question: should I use an invariant culture in this case, or rather en-US ?)

ETA: Well, what I'm doing here is something like the following:

 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)) ) } 

Just a little automation for things that I usually record all the time, and double values โ€‹โ€‹should use the decimal point instead of the comma (which they use in my locale).

ETA2: Interesting little things to add:

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

By putting a variable in a string, the locale of the set does not seem in any way.

+6
powershell number-formatting locale
Mar 04 '10 at 13:21
source share
4 answers

This is the PowerShell function that I use to test the script in other cultures. I believe that it can be used for you:

 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 
+9
Mar 04 '10 at 15:04
source share

While Keith Hill's helpful answer shows you how to change the current script culture on demand (a more modern alternative to PSV3 + and the .NET framework v4.6 +:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture ), you donโ€™t need to change the culture , because - as you found in your second update, the question is that the PowerShell syntax line (as opposed to using -f ) always uses the invariant, and not the current culture :

In other words:

If you replace 'val: {0}' -f 1.2 with "val: $(1.2)" , the literal 1.2 will not be formatted according to the rules of the current culture.
You can check in the console by doing (in one line: PSv3 +,. NET framework v4.6 +):

  > [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. 



Background:

Surprisingly, PowerShell always applies the invariant rather than the current culture in the following string contexts if the type at hand supports culture-oriented conversion to and from strings:

Based in part on this detailed answer , PowerShell explicitly requests culture-independent processing by passing a [cultureinfo]::InvariantCulture instance in the following scenarios:

  • When the string is interpolation : if the object type implements the IFormattable interface; otherwise, PowerShell calls .psobject.ToString() on the object.

  • When casting :

    • to a string , including when binding to a [string] -typed parameter : if the source type implements the [IFormattable] interface; otherwise, PowerShell calls .psobject.ToString() .
    • from the line : if the static .Parse() method of the target type has an overload with [IFormatProvider] -typed (which is the interface implemented by [cultureinfo] ).
  • When comparing strings ( -eq , -lt , -gt ) using String.Compare() , which takes a CultureInfo parameter .

  • Others?

Regarding the fact that the invariant culture is / for:

Invariant culture is insensitive to culture; It is associated with the English language, but not with any country / region .
[...]
Unlike culture-sensitive data, which can be changed using customization or updates in the .NET Framework or operating system, invariant culture data is stable over time and in established cultures and cannot be changed by users. This makes an invariant culture particularly useful for operations that require culture-independent results, such as formatting and parsing operations that store formatted data or sort and order operations that require that data be displayed in a fixed order regardless of culture.

Presumably, stability in cultures has prompted PowerShell designers to consistently use an invariant culture when they are implicitly converted to and from strings .

For example, if you hard-code a date string, such as '7/21/2017' , into a script, and then try to convert it to date using the [date] cast, PowerShell culture-specific behavior ensures that the script is not interrupted even at startup, while a culture other than the US English is valid - fortunately, the invariant culture also recognizes ISO 8601 date and time strings .
for example, [datetime] '2017-07-21' .

On the other hand, if you want to convert strings and corresponding strings corresponding to the current culture, you must do this explicitly .

Summarizing:

  • Convert to strings :

    • Embedding instances of data types with representations of the string depending on the default culture inside "..." results in displaying an invariant culture ( [double] or [datetime] are examples of such types).
    • To get an idea of โ€‹โ€‹the current culture, call .ToString() explicitly or use the -f format operator (possibly inside "..." with $(...) ).
  • Convert from strings :

    • Live broadcasting ( [<type>] ... ) only ever recognizes culture-invariant string representations.

    • To convert from a string representation that matches the current culture (or a specific culture representation), use the target type static method ::Parse() explicitly (optionally with an explicit instance of [cultureinfo] to represent a particular culture).




Examples of "Culture-INVARIANT":

  • string interpolation and cast :

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

      • both output a string literal of 0.1 , with a decimal mark . , regardless of the current culture.
    • Similarly, line castings are culturally invariant; for example, [double] '1.2'

      • . always recognized as a decimal mark, regardless of the current culture.
      • Another way: [double] 1.2 not translated into the overload [double]::Parse('1.2') method, depending on the culture, by default, but to the cultural invariant [double]::Parse('1.2', [cultureinfo]::InvariantCulture)
  • string comparison (suppose [cultureinfo]::CurrentCulture='tr-TR' is valid - Turkish, where i NOT a string representation of i )

    • [string]::Equals('i', 'I', 'CurrentCultureIgnoreCase')
      • $false with current Turkish culture.
      • 'i'.ToUpper() shows that in Turkish culture the upper case is ฤฐ , not i .
    • 'i' -eq 'I'
      • still $true since invariant culture is applied.
      • implicitly the same as: [string]::Equals('i', 'I', 'InvariantCultureIgnoreCase')



Culture Examples: SENSITIVE:

The current culture is observed in the following cases:

  • C -f string format operator (as above):

    • [cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2 [cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2 gives 1,2
    • Pitfall: due to operator precedence, any expression like RHS -f must be enclosed in (...) in order to be recognized as such:
      • For example, '{0}' -f 1/10 is evaluated as specified ('{0}' -f 1) / 10 ,
        Use '{0}' -f (1/10) instead.
  • The default output to the console is :

    • for example, [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 gives 1,2

    • The same applies to the conclusions of the cmdlets; eg,
      [cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01' [cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01' gives Sonntag, 1. Januar 2017 00:00:00

    • Warning. There seems to be a bug with Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.5: in certain scripts, literals are passed to the script block, since unlimited parameters can lead to culturally invariant default output - see this GitHub issue

  • When writing to a file with Set-Content / Add-Content or Out-File / > / >> :

    • for example, [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 gives 1,2
  • When using the static methods ::Parse() / ::TryParse() for number types such as [double] when passing only strings for parsing; for example, with a culture fr-FR (where , denotes a decimal place), [double]::Parse('1,2') returns double 1.2 (ie 1 + 2/10 ).

    • Warning. According to bviktor , thousands separators are recognized by default, but very easily: efficiently, a thousand separator can be placed anywhere inside the integer part, regardless of how many digits are in the resulting groups, and the leading 0 also accepted; for example, in en-US culture (where , is the thousands separator), [double]::Parse('0,18') may succeed unexpectedly and give 18 .
      • To suppress recognition of thousands separators, use something like [double]::Parse('0,18', 'Float') using the NumberStyles parameter
  • Others?

+6
Jun 02 '16 at 23:29
source share

I thought about how to do it easily and came up with accelerators:

 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() 

Hope this helps.

+4
Mar 04 '10 at 14:40
source share

If you already have a culture loaded in your environment,

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

can solve this problem:

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

like this:

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

Of course, you need to keep in mind that if other users run the script with a different locale setting, the results may not work out as originally intended.

However, this solution may come in handy. Enjoy!

+1
Feb 24 '17 at 10:35
source share



All Articles