PowerShell,格式化另一种文化中的值

Joe*_*oey 8 powershell locale number-formatting

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

是否有一种简单的方法可以为函数设置另一个区域设置,而不会粘连

.ToString((New-Object Globalization.CultureInfo ""))
Run Code Online (Sandbox Code Playgroud)

每个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))
    )
}
Run Code Online (Sandbox Code Playgroud)

对于我倾向于一直写的东西只是一点点自动化,双值需要使用小数点而不是逗号(它们在我的语言环境中使用).

ETA2:有趣的琐事补充:

PS Home:> $d=1.23
PS Home:> $d
1,23
PS Home:> "$d"
1.23
Run Code Online (Sandbox Code Playgroud)

通过将变量放入字符串中,设置的语言环境似乎不适用于某种程度.

mkl*_*nt0 12

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

换一种说法:

如果替换'val: {0}' -f 1.2"val: $(1.2)",1.2不会根据当前文化的规则格式化文字.
您可以通过运行(在一行 ; 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.
Run Code Online (Sandbox Code Playgroud)

背景:

出人意料的是,PowerShell中始终应用不变的,而不是当前在以下文化字符串相关的上下文,如果手头的类型支持特定文化的转换和从字符串:

正如在这个深入的回答中所解释的,PowerShell[cultureinfo]::InvariantCulture在以下场景中明确要求通过传递实例来进行文化不变处理:

  • 字符串插值:如果对象的类型实现的IFormattable接口; 否则,PowerShell会调用.psobject.ToString()该对象.

  • 铸造:

    • 字符串,包括绑定到[string]-typed参数时:如果源类型实现[IFormattable]接口; 否则,PowerShell调用.psobject.ToString().
    • 来自字符串:如果目标类型的静态.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]是这种类型的示例).
    • 要获取当前文化表示,请.ToString()显式调用或使用-f格式化运算符(可能"..."通过封闭内部$(...)).
  • 字符串转换:

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

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


文化 - INVARIANT示例:

  • 字符串插值强制转换:

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

      • 两者都产生字符串文字0.1,带小数标记.,与当前文化无关.
    • 同样,来自弦乐的演员也是文化不变的; 例如,[double] '1.2'

      • .始终认为是小数点符号,而不用考虑当前文化.
      • 把它的另一种方式:[double] 1.2没有翻译的文化- 敏感逐默认方法重载[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

    • 警告:从Windows PowerShell v5.1/PowerShell Core v6.0.0-beta.5开始,似乎存在一个错误:在某些情况下,作为无约束参数传递给脚本块的文字可能会导致文化不变的默认输出 - 请参阅此GitHub问题

  • 使用/ 或//写入文件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')返回double 1.2(即1 + 2/10).

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

  • 其他?


Kei*_*ill 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
Run Code Online (Sandbox Code Playgroud)

  • Stej,好问题.在命令行键入它并反复执行:`[Threading.Thread] :: CurrentThread.ManagedThreadId`.现在执行:`&{1..5 | %{[Threading.Thread] :: CurrentThread.ManagedThreadId}}`.看来,当您以交互方式执行命令时,无法保证它们将在从一次调用到下一次调用的同一线程上执行.但是在scriptblock中,使用了相同的线程. (5认同)
  • 如果切换到STA(在PowerShell命令行上放置-STA参数),该线程将被重用(但文化更改似乎仍然没有超过单个管道). (3认同)
  • @Jaykul确认.使用`-STA`在单线程单元中启动PowerShell,然后键入(在****行中):`[System.Threading.Thread] :: CurrentThread.CurrentCulture = [System.Globalization.CultureInfo]"ar-SA "; [DATETIME] ::现在; [System.Threading.Thread] :: CurrentThread.ManagedThreadId;`将文化更改为阿拉伯语(沙特阿拉伯),以某种阿拉伯语方式写入当前日期和时间,最后写入一个线程ID.但在那之后行`[DateTime] :: Now; [System.Threading.Thread] :: CurrentThread.ManagedThreadId;`将使用原始文化.但是,线程ID是相同的. (2认同)

ste*_*tej 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()
Run Code Online (Sandbox Code Playgroud)

希望对您有所帮助。