12

PowerShell中是否有一种简单的方法可以在另一个语言环境中格式化数字等?我目前正在编写一些函数来简化 SVG 生成,并且 SVG.用作小数分隔符,而 PowerShellde-DE在将浮点数转换为字符串时尊重我的语言环境设置 ( )。

有没有一种简单的方法可以为一个函数设置另一个语言环境而不用坚持

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

在每个double变量之后?

注意:这是关于用于格式化的语言环境,而不是格式字符串。

(附带问题:在这种情况下我应该使用不变的文化还是更确切地说en-US?)

ETA:嗯,我在这里尝试的是如下内容:

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

对我经常写的东西进行一点自动化,双精度值需要使用小数点而不是逗号(它们在我的语言环境中使用)。

ETA2:添加有趣的琐事:

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

通过将变量放入字符串中,设置的语言环境似乎不适用,不知何故。

4

4 回答 4

18

虽然Keith Hill 的有用答案向您展示了如何按需更改脚本的当前文化(PSv3+ 和 .NET 框架 v4.6+ 的更现代替代方案:)
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture,但无需更改文化,因为 - 正如您发现的那样在您对该问题的第二次更新中-PowerShell 的字符串插值(而不是使用-f运算符)始终使用不变量而不是当前区域性

换句话说:

如果替换'val: {0}' -f 1.2为,则"val: $(1.2)"文字不会根据当前区域性的规则进行格式化。 您可以通过运行(在单行上;PSv3+、.NET 框架 v4.6+)在控制台中进行验证:1.2

 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.

背景:

令人惊讶的是,如果手头的类型支持特定于文化的字符串与字符串之间的转换, PowerShell总是在以下与字符串相关的上下文中应用不变量而不是当前文化:

正如在这个深入的答案中所解释的,PowerShell 在以下场景中显式地请求文化不变处理- 通过传递[cultureinfo]::InvariantCulture实例:

  • string-interpolating:如果对象的类型实现了IFormattable接口;否则,PowerShell 会调用.psobject.ToString()该对象。

  • 铸造时:

    • string,包括绑定到[string]-typed 参数时:如果源类型实现了[IFormattable]接口;否则,PowerShell 调用.psobject.ToString().
    • from a string : 如果目标类型的静态.Parse()方法具有带有[IFormatProvider]-typed 参数的重载(这是由 实现的接口[cultureinfo])。
  • 字符串比较( -eq, -lt, -gt) 时,使用String.Compare()接受CultureInfo参数的重载。

  • 其他的?

至于不变文化是/是为了什么:

不变的文化是文化不敏感的;它与英语相关联,但与任何国家/地区无关
[...]
与文化敏感数据不同,文化敏感数据会因用户自定义或 .NET Framework 或操作系统的更新而发生变化,不变的文化数据随着时间的推移和跨已安装的文化是稳定的,并且不能由用户自定义。这使得不变区域性对于需要与区域性无关的结果的操作特别有用,例如保留格式化数据的格式化和解析操作,或要求数据以固定顺序显示而与区域性无关的排序和排序操作。

据推测,正是跨文化的稳定性促使 PowerShell 的设计者在隐式转换字符串时始终使用不变的文化。

例如,如果您将日期字符串(例如脚本)硬编码'7/21/2017'到脚本中,然后尝试使用强制转换将其转换为日期[date],PowerShell 的文化不变行为可确保脚本即使在美国以外的文化运行时也不会中断- 英语有效 -幸运的是,不变文化也识别 ISO 8601 格式的日期和时间字符串
例如,[datetime] '2017-07-21'也可以。

另一方面,如果您确实想与当前适合文化的字符串相互转换,则必须明确地这样做。

总结一下:

  • 转换字符串

    • 在内部嵌入具有默认文化敏感字符串表示的数据类型实例会"..."产生文化不变表示([double]或者[datetime]是此类类型的示例)。
    • 要获得当前的-culture 表示,请.ToString()显式调用或使用-f格式化运算符(可能"..."通过封闭在内部$(...))。
  • 字符串转换

    • 直接转换 ( [<type>] ...) 只识别文化不变的字符串表示。

    • 要从当前适合文化的字符串表示(或特定文化的表示)转换,请显式使用目标类型的静态::Parse()方法(可选地使用显式[cultureinfo]实例来表示特定文化)。


文化不变的例子:

  • 字符串插值强制转换

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

      • 无论当前的文化如何,都产生0.1带小数点的字符串文字。.
    • 类似地,字符串强制转换是文化不变的;例如,[double] '1.2'

      • .始终识别为小数点,与当前的文化无关。
      • 另一种说法:[double] 1.2不是翻译成culture- sensitive -by-default方法重载,而是翻译成culture- invariant[double]::Parse('1.2') [double]::Parse('1.2', [cultureinfo]::InvariantCulture)
  • 字符串比较(假设[cultureinfo]::CurrentCulture='tr-TR'有效 - 土耳其语,其中i不是 的小写表示I

    • [string]::Equals('i', 'I', 'CurrentCultureIgnoreCase')
      • $false土耳其文化的影响。
      • 'i'.ToUpper()表明在土耳其文化中,大写是İ,不是I
    • 'i' -eq 'I'
      • 仍然是$true,因为应用了不变的文化。
      • 隐含地等同于:[string]::Equals('i', 'I', 'InvariantCultureIgnoreCase')

文化敏感的例子:

在以下情况下尊重当前的文化:

  • 使用-f, 字符串格式化运算符(如上所述):

    • [cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2产量1,2
    • 陷阱:由于运算符优先级,任何作为 RHS 的表达式-f都必须包含在其中(...)才能被识别为:
      • 例如,'{0}' -f 1/10被评估为好像('{0}' -f 1) / 10已被指定;
        改为使用'{0}' -f (1/10)
  • 默认输出到控制台

    • 例如,[cultureinfo]::CurrentCulture = 'de-DE'; 1.2产量1,2

    • 这同样适用于 cmdlet 的输出;例如,
      [cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01'产量
      Sonntag, 1. Januar 2017 00:00:00

    • 警告:在某些情况下,作为不受约束的参数传递给脚本块的文字可能会导致文化不变的默认输出 - 请参阅GitHub 问题 #4557GitHub 问题 #4558

  • 使用/或/ /写入文件Set-ContentAdd-ContentOut-File>>>时:

    • 例如,[cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt产量1,2
  • 在数字类型上使用静态::Parse()/::TryParse()方法时,例如[double]仅传递要解析的字符串时;例如,如果文化fr-FR有效(,小数点在哪里),则[double]::Parse('1,2')返回双精度1.2(即1 + 2/10)。

    • 警告:正如bviktor指出的那样,默认情况下会识别千位分隔符,但以一种非常松散的方式:实际上,千位分隔符可以放置在整数部分内的任何位置,而不管结果组中有多少位,并且前导0是也接受;例如,在en-US文化中(,千位分隔符在哪里),[double]::Parse('0,18')可能会出人意料地成功并产生18.
      • [double]::Parse('0,18', 'Float')要禁止识别千位分隔符,请通过参数使用 , 之类的东西NumberStyles
  • 无意的文化敏感性,不会被纠正以保持向后兼容性

  • 其他的?

于 2016-06-02T23:29:59.417 回答
11

这是我用于在其他文化中测试脚本的 PowerShell 函数。我相信它可以用于您所追求的:

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
于 2010-03-04T15:04:28.683 回答
5

我正在考虑如何让它变得简单并想出了加速器:

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

我希望它会有所帮助。

于 2010-03-04T14:40:11.393 回答
2

如果您已经在环境中加载了文化,

    #>Get-Culture
    LCID             Name             DisplayName                                                                                                                                             
----             ----             -----------                                                                                                                                             
1031             de-DE            German (Germany)                                                                                                                                        

#>Get-UICulture

LCID             Name             DisplayName                                                                                                                                             
----             ----             -----------                                                                                                                                             
1033             en-US            English (United States) 

有可能解决这个问题:

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

像这样:

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

当然,您需要记住,如果其他用户使用不同的语言环境设置运行脚本,结果可能不会像最初预期的那样。

然而,这个解决方案可能会派上用场。玩得开心!

于 2017-02-24T10:35:51.810 回答