mirror of
https://github.com/PowerShell/PowerShell
synced 2026-06-08 12:12:50 +00:00
Render decimal numbers in a table using current culture (#17650)
This commit is contained in:
+1
@@ -361,6 +361,7 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
|
||||
internal sealed class FieldFormattingDirective
|
||||
{
|
||||
internal string formatString = null; // optional
|
||||
internal bool isTable = false;
|
||||
}
|
||||
|
||||
#endregion Elementary Tokens
|
||||
|
||||
@@ -483,6 +483,12 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
|
||||
directive = activeAssociationList[k].OriginatingParameter.GetEntry(FormatParameterDefinitionKeys.FormatStringEntryKey) as FieldFormattingDirective;
|
||||
}
|
||||
|
||||
if (directive is null)
|
||||
{
|
||||
directive = new FieldFormattingDirective();
|
||||
directive.isTable = true;
|
||||
}
|
||||
|
||||
fpf.propertyValue = this.GetExpressionDisplayValue(so, enumerationLimit, this.activeAssociationList[k].ResolvedExpression, directive);
|
||||
tre.formatPropertyFieldList.Add(fpf);
|
||||
}
|
||||
|
||||
+51
-28
@@ -208,8 +208,9 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
|
||||
/// <param name="expressionFactory">Expression factory to create PSPropertyExpression.</param>
|
||||
/// <param name="enumerationLimit">Limit on IEnumerable enumeration.</param>
|
||||
/// <param name="formatErrorObject">Stores errors during string conversion.</param>
|
||||
/// <param name="formatFloat">Determine if to format floating point numbers using current culture.</param>
|
||||
/// <returns>String representation.</returns>
|
||||
internal static string SmartToString(PSObject so, PSPropertyExpressionFactory expressionFactory, int enumerationLimit, StringFormatError formatErrorObject)
|
||||
internal static string SmartToString(PSObject so, PSPropertyExpressionFactory expressionFactory, int enumerationLimit, StringFormatError formatErrorObject, bool formatFloat = false)
|
||||
{
|
||||
if (so == null)
|
||||
return string.Empty;
|
||||
@@ -294,7 +295,23 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// take care of the case there is no base object
|
||||
if (formatFloat && so.BaseObject is not null)
|
||||
{
|
||||
// format numbers using the current culture
|
||||
if (so.BaseObject is double dbl)
|
||||
{
|
||||
return dbl.ToString("F");
|
||||
}
|
||||
else if (so.BaseObject is float f)
|
||||
{
|
||||
return f.ToString("F");
|
||||
}
|
||||
else if (so.BaseObject is decimal d)
|
||||
{
|
||||
return d.ToString("F");
|
||||
}
|
||||
}
|
||||
|
||||
return so.ToString();
|
||||
}
|
||||
catch (Exception e) when (e is ExtendedTypeSystemException || e is InvalidOperationException)
|
||||
@@ -333,43 +350,49 @@ namespace Microsoft.PowerShell.Commands.Internal.Format
|
||||
StringFormatError formatErrorObject, PSPropertyExpressionFactory expressionFactory)
|
||||
{
|
||||
PSObject so = PSObjectHelper.AsPSObject(val);
|
||||
if (directive != null && !string.IsNullOrEmpty(directive.formatString))
|
||||
bool isTable = false;
|
||||
if (directive is not null)
|
||||
{
|
||||
// we have a formatting directive, apply it
|
||||
// NOTE: with a format directive, we do not make any attempt
|
||||
// to deal with IEnumerable
|
||||
try
|
||||
isTable = directive.isTable;
|
||||
if (!string.IsNullOrEmpty(directive.formatString))
|
||||
{
|
||||
// use some heuristics to determine if we have "composite formatting"
|
||||
// 2004/11/16-JonN This is heuristic but should be safe enough
|
||||
if (directive.formatString.Contains("{0") || directive.formatString.Contains('}'))
|
||||
// we have a formatting directive, apply it
|
||||
// NOTE: with a format directive, we do not make any attempt
|
||||
// to deal with IEnumerable
|
||||
try
|
||||
{
|
||||
// we do have it, just use it
|
||||
return string.Format(CultureInfo.CurrentCulture, directive.formatString, so);
|
||||
// use some heuristics to determine if we have "composite formatting"
|
||||
// 2004/11/16-JonN This is heuristic but should be safe enough
|
||||
if (directive.formatString.Contains("{0") || directive.formatString.Contains('}'))
|
||||
{
|
||||
// we do have it, just use it
|
||||
return string.Format(CultureInfo.CurrentCulture, directive.formatString, so);
|
||||
}
|
||||
// we fall back to the PSObject's IFormattable.ToString()
|
||||
// pass a null IFormatProvider
|
||||
return so.ToString(directive.formatString, formatProvider: null);
|
||||
}
|
||||
// we fall back to the PSObject's IFormattable.ToString()
|
||||
// pass a null IFormatProvider
|
||||
return so.ToString(directive.formatString, null);
|
||||
}
|
||||
catch (Exception e) // 2004/11/17-JonN This covers exceptions thrown in
|
||||
// string.Format and PSObject.ToString().
|
||||
// I think we can swallow these.
|
||||
{
|
||||
// NOTE: we catch all the exceptions, since we do not know
|
||||
// what the underlying object access would throw
|
||||
if (formatErrorObject != null)
|
||||
catch (Exception e) // 2004/11/17-JonN This covers exceptions thrown in
|
||||
// string.Format and PSObject.ToString().
|
||||
// I think we can swallow these.
|
||||
{
|
||||
formatErrorObject.sourceObject = so;
|
||||
formatErrorObject.exception = e;
|
||||
formatErrorObject.formatString = directive.formatString;
|
||||
return string.Empty;
|
||||
// NOTE: we catch all the exceptions, since we do not know
|
||||
// what the underlying object access would throw
|
||||
if (formatErrorObject is not null)
|
||||
{
|
||||
formatErrorObject.sourceObject = so;
|
||||
formatErrorObject.exception = e;
|
||||
formatErrorObject.formatString = directive.formatString;
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we do not have a formatting directive or we failed the formatting (fallback)
|
||||
// but we did not report as an error;
|
||||
// this call would deal with IEnumerable if the object implements it
|
||||
return PSObjectHelper.SmartToString(so, expressionFactory, enumerationLimit, formatErrorObject);
|
||||
return PSObjectHelper.SmartToString(so, expressionFactory, enumerationLimit, formatErrorObject, isTable);
|
||||
}
|
||||
|
||||
private static PSMemberSet MaskDeserializedAndGetStandardMembers(PSObject so)
|
||||
|
||||
@@ -209,6 +209,27 @@ dbda : KM
|
||||
$actual = $actual -replace "`r`n", "`n"
|
||||
$actual | Should -BeExactly $expected
|
||||
}
|
||||
|
||||
It 'Float, double, and decimal should not be truncated to number of decimals from current culture' {
|
||||
$o = [PSCustomObject]@{
|
||||
double = [double]1234.56789
|
||||
float = [float]9876.543
|
||||
decimal = [decimal]4567.123456789
|
||||
}
|
||||
|
||||
$expected = @"
|
||||
|
||||
double : 1234.56789
|
||||
float : 9876.543
|
||||
decimal : 4567.123456789
|
||||
|
||||
|
||||
|
||||
"@
|
||||
|
||||
$actual = $o | Format-List | Out-String
|
||||
($actual.Replace("`r`n", "`n")) | Should -BeExactly ($expected.Replace("`r`n", "`n"))
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Format-List color tests' {
|
||||
|
||||
@@ -845,6 +845,25 @@ A Name B
|
||||
$actual = $obj | Format-Table | Out-String
|
||||
($actual.Replace("`r`n", "`n")) | Should -BeExactly ($expected.Replace("`r`n", "`n"))
|
||||
}
|
||||
|
||||
It 'Table should format floats, doubles, and decimals with number of decimals from current culture' {
|
||||
$o = [PSCustomObject]@{
|
||||
double = [double]1234.56789
|
||||
float = [float]9876.54321
|
||||
decimal = [decimal]4567.123456789
|
||||
}
|
||||
|
||||
$table = $o | Format-Table | Out-String
|
||||
|
||||
$line = foreach ($line in $table.split([System.Environment]::NewLine)) { if ($line -match '^1234') { $line } }
|
||||
$line | Should -Not -BeNullOrEmpty
|
||||
$expectedDecimals = (Get-Culture).NumberFormat.NumberDecimalDigits
|
||||
|
||||
foreach ($num in $line.split(' ', [System.StringSplitOptions]::RemoveEmptyEntries)) {
|
||||
$numDecimals = $num.length - $num.indexOf((Get-Culture).NumberFormat.NumberDecimalSeparator) - 1
|
||||
$numDecimals | Should -Be $expectedDecimals -Because $num
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Describe 'Table color tests' {
|
||||
|
||||
Reference in New Issue
Block a user