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.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]
при передаче только строки для разбора; например, с помощью 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
конечно, вам нужно иметь в виду, что если другие пользователи запускают сценарий с другой настройкой локали, результаты могут оказаться не так, как изначально предполагалось.
тем не менее, это решение может пригодиться. Получайте удовольствие!