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]).
 
- 
to строка, в том числе, когда привязка к 
- , когда строка-сравнение ( - -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.2is не в переводе на язык культуры -чувствительныйпо умолчанию способ перегрузки[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]при передаче только строки для разбора; например, с помощью culturefr-FRпо сути (где,это десятичный знак),[double]::Parse('1,2')возвращает double1.2(т. е.1 + 2/10).- 
предостережение: As bviktor указывает, разделители тысяч признаны по умолчанию, но очень свободно: эффективно можно разместить разделитель тысяч в любом месте внутри целочисленной части, независимо от того, сколько цифр в результирующих группах, и ведущий 0также принимаются, например, вen-USкультура (где,является разделителем тысяч),[double]::Parse('0,18')возможно, удивительно успешно и урожайности18.- для подавления распознавания тысяч разделителей используйте что - то вроде [double]::Parse('0,18', 'Float'), черезNumberStylesпараметр
 
- для подавления распознавания тысяч разделителей используйте что - то вроде 
 
- 
предостережение: As bviktor указывает, разделители тысяч признаны по умолчанию, но очень свободно: эффективно можно разместить разделитель тысяч в любом месте внутри целочисленной части, независимо от того, сколько цифр в результирующих группах, и ведущий 
- другим? 
Я думал о том, как сделать это легко и придумали ускорители:
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
конечно, вам нужно иметь в виду, что если другие пользователи запускают сценарий с другой настройкой локали, результаты могут оказаться не так, как изначально предполагалось.
тем не менее, это решение может пригодиться. Получайте удовольствие!
