I have a string,
$string = "2,55"
How to convert this string to decimal?
In short -
[decimal]$string.Replace(",", ".")
Another way of converting this (not necessarily better) is using the ToDecimal method with a specific culture. Here I'm using the standard french culture.
[System.Convert]::ToDecimal("2,55",[cultureinfo]::GetCultureInfo('fr-FR'))
2.55
You should convert using the current locale. Replacing , with . isn't reliable and is slightly slower (because another string must be created). Both Parse and TryParse have a culture parameter that you can use
PS D:\> $string = "2,55"
PS D:\> $culture = Get-Culture
PS D:\> [decimal]::Parse($string, $culture)
2.55
PS D:\> [decimal]$dec = 0
PS D:\> [decimal]::TryParse($string, [Globalization.NumberStyles]::Float, $culture, [ref]$dec)
True
PS D:\> $dec
2.55
If you know the locale of the input then parse directly in that locale by using GetCultureInfo
$culture = [cultureinfo]::GetCultureInfo('fr-FR')
Note that if the string contains the exponent like "2,55e2" then none of the current answers actually work (although it may appears to work). For more details read How can I convert a string such as 5.7303333333e+02 to decimal in PowerShell?
To force a conversion to a specific datatype, prefix the value or variable with the type in square brackets, this is known as a Cast Operator and forces the chosen datatype:
$string = "100.5"
$decimal = [decimal]$string
$string + 0.5
# Outputs 100.10.5
$decimal + 0.5
# Outputs 101,0
More information can be found here
Related
Is there an easy way in PowerShell to format numbers and the like in another locale? I'm currently writing a few functions to ease SVG generation for me and SVG uses . as a decimal separator, while PowerShell honors my locale settings (de-DE) when converting floating-point numbers to strings.
Is there an easy way to set another locale for a function or so without sticking
.ToString((New-Object Globalization.CultureInfo ""))
after every double variable?
Note: This is about the locale used for formatting, not the format string.
(Side question: Should I use the invariant culture in that case or rather en-US?)
ETA: Well, what I'm trying here is something like the following:
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))
)
}
Just a little automation for stuff I tend to write all the time and the double values need to use the decimal point instead of a comma (which they use in my locale).
ETA2: Interesting trivia to add:
PS Home:> $d=1.23
PS Home:> $d
1,23
PS Home:> "$d"
1.23
By putting the variable into a string the set locale doesn't seem to apply, somehow.
While Keith Hill's helpful answer shows you how to change a script's current culture on demand (more modern alternative as of PSv3+ and .NET framework v4.6+:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture), there is no need to change the culture, because - as you've discovered in your second update to the question - PowerShell's string interpolation - as opposed to using the -f operator - always uses the invariant rather than the current culture:
In other words:
If you replace 'val: {0}' -f 1.2 with "val: $(1.2)", the number literal 1.2 is not formatted according to the rules of the current culture.
You can verify in the console by running (on a single line; 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.
Note: In PowerShell (Core) 7+, the change to a different culture remains in effect for the remainder of the session (as it arguably should for Windows PowerShell too, but doesn't).
Background:
By design,[1] but perhaps surprisingly, PowerShell applies the invariant rather than the current culture in the following string-related contexts, if the type at hand supports culture-specific conversion to and from strings:
As explained in this in-depth answer, PowerShell explicitly requests culture-invariant processing, if possible - by passing the [cultureinfo]::InvariantCulture instance - in the following scenarios (the stringification PowerShell performs is the equivalent of calling .psobject.ToString([NullString]::Value, [cultureinfo]::InvariantCulture) on a value):
When string-interpolating: if the object's type implements the IFormattable interface.
When casting:
to a string, including implicit conversion when binding to a [string]-typed parameter: if the source type implements the [IFormattable] interface.
from a string: if the target type's static .Parse() method has an overload with an [IFormatProvider]-typed parameter (which is an interface implemented by [cultureinfo]).
When string-comparing (-eq, -lt, -gt) , using a String.Compare() overload that accepts a CultureInfo parameter.
Others?
Note that, separately, custom stringification is applied in casts / implicit stringification for the following .NET types:
Arrays and, more generally, similar list-like collection types that PowerShell enumerates in the pipeline (see the bottom section of this answer for what those types are).
The (stringified) elements of such types are concatenated with spaces (strictly speaking: with the string specified in the rarely used $OFS preference variable); the stringification of the elements is recursively subject to the rules described here.
E.g, [string] (1, 2) yields '1 2'
[pscustomobject]
Such instances result in a hashtable-like string format described in this answer; e.g.:
# -> '#{foo=1; bar=2.2}'; values are formatted with the *invariant* culture
[string] ([pscustomobject] #{ foo = 1; bar = 2.2 })
The fact that calling .ToString() directly on a [pscustomobject] instance does not yield this representation and instead returns the empty string should be considered a bug - see GitHub issue #6163.
Others?
As for the purpose of the invariant culture:
The invariant culture is culture-insensitive; it is associated with the English language but not with any country/region.
[...]
Unlike culture-sensitive data, which is subject to change by user customization or by updates to the .NET Framework or the operating system, invariant culture data is stable over time and across installed cultures and cannot be customized by users. This makes the invariant culture particularly useful for operations that require culture-independent results, such as formatting and parsing operations that persist formatted data, or sorting and ordering operations that require that data be displayed in a fixed order regardless of culture.
Presumably, it is the stability across cultures that motivated PowerShell's designers to consistently use the invariant culture when implicitly converting to and from strings.
For instance, if you hard-code a date string such as '7/21/2017' into a script and later try to convert it to date with a [date] cast, PowerShell's culture-invariant behavior ensures that the script doesn't break even when run while a culture other than US-English is in effect - fortunately, the invariant culture also recognizes ISO 8601-format date and time strings;
e.g., [datetime] '2017-07-21' works too.
On the flip side, if you do want to convert to and from current-culture-appropriate strings, you must do so explicitly.
To summarize:
Converting to strings:
Embedding instances of data types with culture-sensitive-by-default string representations inside "..." yields a culture-invariant representation ([double] or [datetime] are examples of such types).
To get a current-culture representation, call .ToString() explicitly or use -f), the formatting operator (possibly inside "..." via an enclosing $(...)).
Converting from strings:
A direct cast ([<type>] ...) only ever recognizes culture-invariant string representations.
To convert from a current-culture-appropriate string representation (or a specific culture's representation), use the target type's static ::Parse() method explicitly (optionally with an explicit [cultureinfo] instance to represent a specific culture).
Culture-INVARIANT examples:
string interpolation and casts:
"$(1/10)" and [string] 1/10
both yield string literal 0.1, with decimal mark ., irrespective of the current culture.
Similarly, casts from strings are culture-invariant; e.g., [double] '1.2'
. is always recognized as the decimal mark, irrespective of the current culture.
Another way of putting it: [double] 1.2 is not translated to the culture-sensitive-by-default method overload [double]::Parse('1.2'), but to the culture-invariant [double]::Parse('1.2', [cultureinfo]::InvariantCulture)
string comparison (assume that [cultureinfo]::CurrentCulture='tr-TR' is in effect - Turkish, where i is NOT a lowercase representation of I)
[string]::Equals('i', 'I', 'CurrentCultureIgnoreCase')
$false with the Turkish culture in effect.
'i'.ToUpper() shows that in the Turkish culture the uppercase is İ, not I.
'i' -eq 'I'
is still $true, because the invariant culture is applied.
implicitly the same as: [string]::Equals('i', 'I', 'InvariantCultureIgnoreCase')
Culture-SENSITIVE examples:
The current culture IS respected in the following cases:
With -f, the string-formatting operator (as noted above):
[cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2 yields 1,2
Pitfall: Due to operator precedence, any expression as the RHS of -f must be enclosed in (...) in order to be recognized as such:
E.g., '{0}' -f 1/10 is evaluated as if ('{0}' -f 1) / 10 had been specified;
use '{0}' -f (1/10) instead.
Default output to the console:
e.g., [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 yields 1,2
The same applies to output from cmdlets; e.g.,
[cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01' yields
Sonntag, 1. Januar 2017 00:00:00
Caveat: In certain scenarios, literals passed to a script block as unconstrained parameters can result in culture-invariant default output - see GitHub issue #4557 and GitHub issue #4558.
In (all?) cmdlets:
Those that that perform equality comparisons:
Select-Object with the -Unique switch; also note that - unusually - case-sensitive comparison is performed, and as of PowerShell 7.2.4 case-insensitivity isn't even available as an opt-in - see GitHub issue #12059.
Select-Object
Compare-Object
Others?
Those that write to files:
Set-Content and Add-Content
Out-File and therefore its virtual alias, > (and >>)
e.g., [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt yields 1,2
Due to .NET's logic, when using the static ::Parse() / ::TryParse() methods on number types such as [double] while passing only the string to parse; e.g., with culture fr-FR in effect (where , is the decimal mark), [double]::Parse('1,2') returns double 1.2 (i.e., 1 + 2/10).
Caveat: As bviktor points out, thousands separators are recognized by default, but in a very loose fashion: effectively, the thousands separator can be placed anywhere inside the integer portion, irrespective of how many digits are in the resulting groups, and a leading 0 is also accepted; e.g., in the en-US culture (where , is the thousands separator), [double]::Parse('0,18') perhaps surprisingly succeeds and yields 18.
To suppress recognition of thousands separators, use something like [double]::Parse('0,18', 'Float'), via the NumberStyles parameter
Unintentional culture-sensitivity that won't be corrected to preserve backward compatibility:
In parameter-binding type conversions for compiled cmdlets (but PowerShell code - scripts or functions - is culture-invariant) - see GitHub issue #6989.
In the -as operator - see GitHub issue #8129.
In [hashtable] key lookups - see this answer and GitHub issue #8280.
[Fixed in v7.1+] In the LHS of -replace operations - see GitHub issue #10948.
Others?
[1] The aim is to support programmatic processing using representations that do not vary by culture and do not change over time. See the linked quote from the docs later in the answer.
This is a PowerShell function I use for testing script in other cultures. I believe it could be used for what you are after:
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
I was thinking about how to make it easy and came up with accelerators:
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()
I hope it will help.
If you already have the culture loaded in your environment,
#>Get-Culture
LCID Name DisplayName
---- ---- -----------
1031 de-DE German (Germany)
#>Get-UICulture
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
it is possible to resolve this problem:
PS Home:> $d=1.23
PS Home:> $d
1,23
like this:
$d.ToString([cultureinfo]::CurrentUICulture)
1.23
Of course you need to keep in mind that if other users run the script with a different locale setting, the results may not turn out as originally intended.
Nevertheless, this solution could come in useful. Have fun!
I have a list with dates inside
$date = #('05-28-2020','09-30-2021')
I want to find the most recent date
I tried to convert string before comparat it :
[datetime]::ParseExact($date[0],'MM_dd_yyyy', $null)
But not working
Any ideas ?
You can directly cast your date-time strings to [datetime] - no need for a ParseExact() call - given that your input format is recognized by the invariant culture that PowerShell always uses in casts.
System.Linq.Enumerable.Max<T>() allows you to find the maximum in a [datetime]-typed enumerable.
Therefore:
$date = #('05-28-2020','09-30-2021') # Note: The #(...) enclosure isn't strictly needed.
[Linq.Enumerable]::Max([datetime[]] $date)
On a US-English system, the above yields Thursday, September 30, 2021 12:00:00 AM, i.e. the default output formatting of the most recent date in the input, [datetime] '09-30-2021'.
As for what you tried:
As has been mentioned, the only immediate problem with your ParseExact() call was that you used _ instead of -.
Also note that passing $null as the third argument implies that the culture currently in effect is used during parsing (as reflected in [cultureinfo]::CurrentCulture; this won't matter with numeric formatting sequences such as MM, but it would with symbolic ones such as MMM - and potentially even with unquoted placeholders : and /.
switch your _ to - and it will work:
$date = #('05-28-2020','09-30-2021')
$max = '01-01-1999'
$date | ForEach-Object {
if($_ -ge $max){
$max = $_
}
}
This should do the work.
can you please tell me how to remove currency formatting from a variable (which is probably treated as a string).
How do I strip out currency formatting from a variable and convert it to a true number?
Thank you.
example
PS C:\Users\abc> $a=($464.00)
PS C:\Users\abc> "{0:N2}" -f $a
<- returns blank
However
PS C:\Users\abc> $a=-464
PS C:\Users\abc> "{0:C2}" -f $a
($464.00) <- this works
PowerShell, the programming language, does not "know" what money or currency is - everything PowerShell sees is a variable name ($464) and a property reference (.00) that doesn't exist, so $a ends up with no value.
If you have a string in the form: $00.00, what you can do programmatically is:
# Here is my currency amount
$mySalary = '$500.45'
# Remove anything that's not either a dot (`.`), a digit, or parentheses:
$mySalary = $mySalary -replace '[^\d\.\(\)]'
# Check if input string has parentheses around it
if($mySalary -match '^\(.*\)$')
{
# remove the parentheses and add a `-` instead
$mySalary = '-' + $mySalary.Trim('()')
}
So far so good, now we have the string 500.45 (or -500.45 if input was ($500.45)).
Now, there's a couple of things you can do to convert a string to a numerical type.
You could explicitly convert it to a [double] with the Parse() method:
$mySalaryNumber = [double]::Parse($mySalary)
Or you could rely on PowerShell performing an implicit conversion to an appropriate numerical type with a unary +:
$mySalaryNumber = +$mySalary
Is there an easy way in PowerShell to format numbers and the like in another locale? I'm currently writing a few functions to ease SVG generation for me and SVG uses . as a decimal separator, while PowerShell honors my locale settings (de-DE) when converting floating-point numbers to strings.
Is there an easy way to set another locale for a function or so without sticking
.ToString((New-Object Globalization.CultureInfo ""))
after every double variable?
Note: This is about the locale used for formatting, not the format string.
(Side question: Should I use the invariant culture in that case or rather en-US?)
ETA: Well, what I'm trying here is something like the following:
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))
)
}
Just a little automation for stuff I tend to write all the time and the double values need to use the decimal point instead of a comma (which they use in my locale).
ETA2: Interesting trivia to add:
PS Home:> $d=1.23
PS Home:> $d
1,23
PS Home:> "$d"
1.23
By putting the variable into a string the set locale doesn't seem to apply, somehow.
While Keith Hill's helpful answer shows you how to change a script's current culture on demand (more modern alternative as of PSv3+ and .NET framework v4.6+:
[cultureinfo]::CurrentCulture = [cultureinfo]::InvariantCulture), there is no need to change the culture, because - as you've discovered in your second update to the question - PowerShell's string interpolation - as opposed to using the -f operator - always uses the invariant rather than the current culture:
In other words:
If you replace 'val: {0}' -f 1.2 with "val: $(1.2)", the number literal 1.2 is not formatted according to the rules of the current culture.
You can verify in the console by running (on a single line; 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.
Note: In PowerShell (Core) 7+, the change to a different culture remains in effect for the remainder of the session (as it arguably should for Windows PowerShell too, but doesn't).
Background:
By design,[1] but perhaps surprisingly, PowerShell applies the invariant rather than the current culture in the following string-related contexts, if the type at hand supports culture-specific conversion to and from strings:
As explained in this in-depth answer, PowerShell explicitly requests culture-invariant processing, if possible - by passing the [cultureinfo]::InvariantCulture instance - in the following scenarios (the stringification PowerShell performs is the equivalent of calling .psobject.ToString([NullString]::Value, [cultureinfo]::InvariantCulture) on a value):
When string-interpolating: if the object's type implements the IFormattable interface.
When casting:
to a string, including implicit conversion when binding to a [string]-typed parameter: if the source type implements the [IFormattable] interface.
from a string: if the target type's static .Parse() method has an overload with an [IFormatProvider]-typed parameter (which is an interface implemented by [cultureinfo]).
When string-comparing (-eq, -lt, -gt) , using a String.Compare() overload that accepts a CultureInfo parameter.
Others?
Note that, separately, custom stringification is applied in casts / implicit stringification for the following .NET types:
Arrays and, more generally, similar list-like collection types that PowerShell enumerates in the pipeline (see the bottom section of this answer for what those types are).
The (stringified) elements of such types are concatenated with spaces (strictly speaking: with the string specified in the rarely used $OFS preference variable); the stringification of the elements is recursively subject to the rules described here.
E.g, [string] (1, 2) yields '1 2'
[pscustomobject]
Such instances result in a hashtable-like string format described in this answer; e.g.:
# -> '#{foo=1; bar=2.2}'; values are formatted with the *invariant* culture
[string] ([pscustomobject] #{ foo = 1; bar = 2.2 })
The fact that calling .ToString() directly on a [pscustomobject] instance does not yield this representation and instead returns the empty string should be considered a bug - see GitHub issue #6163.
Others?
As for the purpose of the invariant culture:
The invariant culture is culture-insensitive; it is associated with the English language but not with any country/region.
[...]
Unlike culture-sensitive data, which is subject to change by user customization or by updates to the .NET Framework or the operating system, invariant culture data is stable over time and across installed cultures and cannot be customized by users. This makes the invariant culture particularly useful for operations that require culture-independent results, such as formatting and parsing operations that persist formatted data, or sorting and ordering operations that require that data be displayed in a fixed order regardless of culture.
Presumably, it is the stability across cultures that motivated PowerShell's designers to consistently use the invariant culture when implicitly converting to and from strings.
For instance, if you hard-code a date string such as '7/21/2017' into a script and later try to convert it to date with a [date] cast, PowerShell's culture-invariant behavior ensures that the script doesn't break even when run while a culture other than US-English is in effect - fortunately, the invariant culture also recognizes ISO 8601-format date and time strings;
e.g., [datetime] '2017-07-21' works too.
On the flip side, if you do want to convert to and from current-culture-appropriate strings, you must do so explicitly.
To summarize:
Converting to strings:
Embedding instances of data types with culture-sensitive-by-default string representations inside "..." yields a culture-invariant representation ([double] or [datetime] are examples of such types).
To get a current-culture representation, call .ToString() explicitly or use -f), the formatting operator (possibly inside "..." via an enclosing $(...)).
Converting from strings:
A direct cast ([<type>] ...) only ever recognizes culture-invariant string representations.
To convert from a current-culture-appropriate string representation (or a specific culture's representation), use the target type's static ::Parse() method explicitly (optionally with an explicit [cultureinfo] instance to represent a specific culture).
Culture-INVARIANT examples:
string interpolation and casts:
"$(1/10)" and [string] 1/10
both yield string literal 0.1, with decimal mark ., irrespective of the current culture.
Similarly, casts from strings are culture-invariant; e.g., [double] '1.2'
. is always recognized as the decimal mark, irrespective of the current culture.
Another way of putting it: [double] 1.2 is not translated to the culture-sensitive-by-default method overload [double]::Parse('1.2'), but to the culture-invariant [double]::Parse('1.2', [cultureinfo]::InvariantCulture)
string comparison (assume that [cultureinfo]::CurrentCulture='tr-TR' is in effect - Turkish, where i is NOT a lowercase representation of I)
[string]::Equals('i', 'I', 'CurrentCultureIgnoreCase')
$false with the Turkish culture in effect.
'i'.ToUpper() shows that in the Turkish culture the uppercase is İ, not I.
'i' -eq 'I'
is still $true, because the invariant culture is applied.
implicitly the same as: [string]::Equals('i', 'I', 'InvariantCultureIgnoreCase')
Culture-SENSITIVE examples:
The current culture IS respected in the following cases:
With -f, the string-formatting operator (as noted above):
[cultureinfo]::currentculture = 'de-DE'; '{0}' -f 1.2 yields 1,2
Pitfall: Due to operator precedence, any expression as the RHS of -f must be enclosed in (...) in order to be recognized as such:
E.g., '{0}' -f 1/10 is evaluated as if ('{0}' -f 1) / 10 had been specified;
use '{0}' -f (1/10) instead.
Default output to the console:
e.g., [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 yields 1,2
The same applies to output from cmdlets; e.g.,
[cultureinfo]::CurrentCulture = 'de-DE'; Get-Date '2017-01-01' yields
Sonntag, 1. Januar 2017 00:00:00
Caveat: In certain scenarios, literals passed to a script block as unconstrained parameters can result in culture-invariant default output - see GitHub issue #4557 and GitHub issue #4558.
In (all?) cmdlets:
Those that that perform equality comparisons:
Select-Object with the -Unique switch; also note that - unusually - case-sensitive comparison is performed, and as of PowerShell 7.2.4 case-insensitivity isn't even available as an opt-in - see GitHub issue #12059.
Select-Object
Compare-Object
Others?
Those that write to files:
Set-Content and Add-Content
Out-File and therefore its virtual alias, > (and >>)
e.g., [cultureinfo]::CurrentCulture = 'de-DE'; 1.2 > tmp.txt; Get-Content tmp.txt yields 1,2
Due to .NET's logic, when using the static ::Parse() / ::TryParse() methods on number types such as [double] while passing only the string to parse; e.g., with culture fr-FR in effect (where , is the decimal mark), [double]::Parse('1,2') returns double 1.2 (i.e., 1 + 2/10).
Caveat: As bviktor points out, thousands separators are recognized by default, but in a very loose fashion: effectively, the thousands separator can be placed anywhere inside the integer portion, irrespective of how many digits are in the resulting groups, and a leading 0 is also accepted; e.g., in the en-US culture (where , is the thousands separator), [double]::Parse('0,18') perhaps surprisingly succeeds and yields 18.
To suppress recognition of thousands separators, use something like [double]::Parse('0,18', 'Float'), via the NumberStyles parameter
Unintentional culture-sensitivity that won't be corrected to preserve backward compatibility:
In parameter-binding type conversions for compiled cmdlets (but PowerShell code - scripts or functions - is culture-invariant) - see GitHub issue #6989.
In the -as operator - see GitHub issue #8129.
In [hashtable] key lookups - see this answer and GitHub issue #8280.
[Fixed in v7.1+] In the LHS of -replace operations - see GitHub issue #10948.
Others?
[1] The aim is to support programmatic processing using representations that do not vary by culture and do not change over time. See the linked quote from the docs later in the answer.
This is a PowerShell function I use for testing script in other cultures. I believe it could be used for what you are after:
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
I was thinking about how to make it easy and came up with accelerators:
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()
I hope it will help.
If you already have the culture loaded in your environment,
#>Get-Culture
LCID Name DisplayName
---- ---- -----------
1031 de-DE German (Germany)
#>Get-UICulture
LCID Name DisplayName
---- ---- -----------
1033 en-US English (United States)
it is possible to resolve this problem:
PS Home:> $d=1.23
PS Home:> $d
1,23
like this:
$d.ToString([cultureinfo]::CurrentUICulture)
1.23
Of course you need to keep in mind that if other users run the script with a different locale setting, the results may not turn out as originally intended.
Nevertheless, this solution could come in useful. Have fun!
substring complains when I try to limit a string to 10 characters which is not 10 or more characters in length. I know I can test the length but I would like to know if there is a single cmdlet which will do what I need.
PS C:\> "12345".substring(0,5)
12345
PS C:\> "12345".substring(0,10)
Exception calling "Substring" with "2" argument(s): "Index and length must refer to a location within the string.
Parameter name: length"
At line:1 char:18
+ "12345".substring( <<<< 0,10)
Do you need exactly a cmdlet? I wonder why you don't like getting length. If it's part of a script, then it looks fine.
$s = "12345"
$s.substring(0, [System.Math]::Min(10, $s.Length))
Using the substring function has it's limitations and requires you to first capture the length of the string. Granted this does work you can do it without that limitation.
The following will return the first 5 characters of the string
"1234567890"[0..4] -join "" # returns the string '12345'
And this will work on strings that are shorter than desired length
"1234567890"[0..1000] -join "" # returns the string '1234567890'
You can load and use other libraries and use their string functions, for example the visual
basic string functions work nicely for what you want to do
call once per session
>[void][reflection.assembly]::LoadWithPartialName("microsoft.visualbasic")
then use various vb string functions
>[microsoft.visualbasic.strings]::left("12345",10)
12345
or
>[microsoft.visualbasic.strings]::mid("12345",1,10)
12345
The previous answers didn't suit my purposes (no offence!) so I took Denomales suggestion above and rolled it into a function which I thought I'd share:
function Trim-Length {
param (
[parameter(Mandatory=$True,ValueFromPipeline=$True)] [string] $Str
, [parameter(Mandatory=$True,Position=1)] [int] $Length
)
$Str[0..($Length-1)] -join ""
}
Example usages:
"1234567" | Trim-Length 4 # returns: "1234"
"1234" | Trim-Length 99 # returns: "1234"
Thanks to Dmitry for the answer, I turned it into a function and made it so it is 1 based as opposed to 0 based.
function acme-substr ([string]$str, $start, $end) {
$str.substring($start-1, [System.Math]::Min($str.Length-1, $end))
}
> $foo="0"*20
> $foo
00000000000000000000
> acme-substr $foo 1 5
00000