PowerShell, форматирование значений в другой культуре

есть ли простой способ в PowerShell форматировать числа и тому подобное в другой локали? В настоящее время я пишу несколько функций для облегчения генерации SVG для меня и SVG использует . в качестве десятичного разделителя, в то время как PowerShell соблюдает Мои настройки локали (de-DE) при преобразовании чисел с плавающей запятой в строки.

есть ли простой способ установить другую локаль для функции или около того, не вставляя

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

после double переменной?

Примечание.: Речь идет о locale используется для форматирования, не в строке формата.

(побочный вопрос: Должен ли я использовать инвариантную культуру в этом случае или, скорее,en-US?)

ЕТА: Ну, то, что я пытаюсь здесь, это что-то вроде следующего:

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 ответов


это функция 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

пока полезный ответ кита Хилла показывает, как изменить текущую культуру скрипта по требованию (более современная альтернатива с PSv3+ и .NET framework v4.6+:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture), является нет необходимости менять культуру, потому что - как вы обнаружили во втором обновлении вопроса -строка PowerShell интерполяция (в отличие от использования -f оператор) всегда использует инвариант а не the настоящее культура:

другими словами:

если заменить 'val: {0}' -f 1.2 С "val: $(1.2)" буквальный 1.2 is не отформатирован в соответствии с правилами текущей культуры.
Вы можете проверить в консоли, выполнив (на одной строке; 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.

Справочная информация:

удивительно, в PowerShell всегда применение инвариант, а не настоящее культура в следующее string-related условиях, если тип под рукой поддерживает преобразование конкретных культур в строки и из строк:

как поясняется в это углубленный ответ!--89-->, PowerShell явно запрашивает культура-инвариантная обработка - путем передачи [cultureinfo]::InvariantCulture экземпляр - в следующих случаях:

  • , когда строку интерполяции: если тип объекта реализует IFormattable интерфейс; в противном случае, PowerShell вызывает .psobject.ToString() на объект.

  • , когда литье:

    • to строка, в том числе, когда привязка к [string]-введенный параметр: если тип источника реализует [IFormattable] интерфейс; в противном случае, PowerShell вызывает .psobject.ToString().
    • с строка: если целевой тип статический .Parse() метод имеет перегрузку с [IFormatProvider] - типизированный параметр (который является интерфейсом, реализованным [cultureinfo]).
  • , когда строка-сравнение (-eq, -lt, -gt) , С помощью String.Compare() перегрузка, которая принимает CultureInfo параметр.

  • другим?

как инвариантного is / IS for:

инвариантная культура нечувствительна к культуре; она связанный с английским языком, но не с какой-либо страной/регионом.
[...]
В отличие от чувствительных к культуре данных, которые могут быть изменены пользовательской настройкой или обновлениями .NET Фреймворк или операционная система, инвариантные данные культуры стабильный с течением времени и через установленные культуры и не могут быть настроены пользователями. Это делает инвариантную культуру особенно полезной для операций, требующих независимых от культуры результатов, таких как операции форматирования и синтаксического анализа, которые сохраняют форматированные данные, или операции сортировки и упорядочения, которые требуют, чтобы данные отображались в фиксированном порядке независимо от культура.

предположительно, именно стабильность в разных культурах побудила дизайнеров PowerShell последовательно использовать инвариантную культуру, когда имплицитно преобразование из строки.

например, если вы жестко кодировать строки даты, такие как '7/21/2017' в скрипт и позже попробуйте преобразовать его в дату с помощью [date] cast, инвариантное к культуре поведение PowerShell гарантирует, что сценарий не сломается, даже если бегите, пока культура, отличная от нас-английский в действительности -к счастью, инвариантная культура также распознает строки даты и времени ISO 8601-format;
например, [datetime] '2017-07-21' тоже работает.

на оборотной стороне,если вы хотите конвертировать в и из настоящее-соответствующие культуре строки, вы должны сделать это явно.

подведем итоги:

  • преобразование to строки:

    • встраивание экземпляров типов данных с чувствительными к культуре строковыми представлениями по умолчанию внутри "..." урожайность культуры-инвариант представление ([double] или [datetime] примеры таких типов).
    • и настоящее - представление культуры, вызов .ToString() явно или использовать -f, оператор форматирования (возможно, внутри "..." через ограждающие $(...)).
  • преобразование с строки:

    • прямой литой ([<type>] ...) только когда-либо признает культуру -инвариант строковые представления.

    • преобразовать из настоящее - culture-соответствующее строковое представление (или конкретные представление культуры), используйте статический::Parse() метод явно (необязательно с явным [cultureinfo] экземпляр для представления конкретной культуры).


инвариантные примеры культуры:

  • строку интерполяции и гипс:

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

      • оба дают строковый литерал 0.1, с десятичным знаком ., независимо от современная культура.
    • аналогично, гипс с строки являются инвариантными к культуре; например,[double] '1.2'

      • . is всегда признан десятичного знака, независимо от текущей культуры.
      • другими словами: [double] 1.2 is не в переводе на язык культуры -чувствительныйпо умолчанию способ перегрузки [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

    • то же самое относится к выходу из командлетов; например,
      [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-бета.5: в некоторых сценариях литералы, переданные блоку сценария в качестве неограниченных параметров, могут привести к выходу по умолчанию, инвариантному к культуре-см. этот вопрос GitHub

  • при написании на с Set-Content/Add-Content или Out-File / > / >>:

    • например, [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt дает 1,2
  • при использовании static ::Parse() / ::TryParse() методы для типов чисел, таких как [double] при передаче только строки для разбора; например, с помощью culture fr-FR по сути (где , это десятичный знак), [double]::Parse('1,2') возвращает double 1.2 (т. е. 1 + 2/10).

    • предостережение: As bviktor указывает, разделители тысяч признаны по умолчанию, но очень свободно: эффективно можно разместить разделитель тысяч в любом месте внутри целочисленной части, независимо от того, сколько цифр в результирующих группах, и ведущий 0 также принимаются, например, в en-US культура (где , является разделителем тысяч),[double]::Parse('0,18') возможно, удивительно успешно и урожайности 18.
      • для подавления распознавания тысяч разделителей используйте что - то вроде [double]::Parse('0,18', 'Float'), через NumberStyles параметр
  • другим?


Я думал о том, как сделать это легко и придумали ускорители:

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

Я надеюсь, что это поможет.


Если у вас уже есть культура, загруженных в вашей среде,

    #>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

конечно, вам нужно иметь в виду, что если другие пользователи запускают сценарий с другой настройкой локали, результаты могут оказаться не так, как изначально предполагалось.

тем не менее, это решение может пригодиться. Получайте удовольствие!