拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 PowerShell:将数字的字符串表示形式转换为整数

PowerShell:将数字的字符串表示形式转换为整数

白鹭 - 2022-01-23 2228 0 0

我真的很努力地尝试不问这个问题,但我一直回到这个问题,因为我不确定我是否尽可能有效地做每件事,或者是否可能存在问题。基本上,我有一个包含数字栏位的 CSV 档案,但它包含一个小数和十万位的值,例如15.0000. 我需要做的就是将其转换为没有小数位的整数。

我在这里遇到了一个相关的问题,但所选的答案似乎对将字符串表示形式直接转换为整数资料型别产生了怀疑 - 没有解释原因。

简单地将字符串转换为 anint不会可靠地作业。您需要将其转换为int32.

我没有太多运气让[System.Convert]方法起作用,或者做类似$StringNumber.ToInt32(). 我意识到,一旦我将资料保存回PSCustomObject它们,它们将被存盘为字符串,所以在一天结束时,也许我使这比我的用例所需的更复杂,我只需要重新格式化$StringNumber......但即使这样也给我带来了一些问题。

关于为什么在我的情况下铸造不可靠或更好的方法来处理这个问题的任何想法?

我尝试过的例子:

PS > $StringNumber = '15.0000'

PS > [Convert]::ToInt32($StringNumber)
#MethodInvocationException: Exception calling "ToInt32" with "1" argument(s): "Input string was not in a correct format."

PS > [Convert]::ToInt32($StringNumber, [CultureInfo]::InvariantCulture)
#MethodInvocationException: Exception calling "ToInt32" with "2" argument(s): "Input string was not in a correct format."

PS > $StringNumber.ToInt32()
#MethodException: Cannot find an overload for "ToInt32" and the argument count: "0".

PS > $StringNumber.ToInt32([CultureInfo]::InvariantCulture)
#MethodInvocationException: Exception calling "ToInt32" with "1" argument(s): "Input string was not in a correct format."

PS > $StringNumber.ToString("F0")
#MethodException: Cannot find an overload for "ToString" and the argument count: "1".

PS > $StringNumber.ToString("F0", [CultureInfo]::CurrentCulture)
#MethodException: Cannot find an overload for "ToString" and the argument count: "2".

PS > "New format: {0:F0}" -f $StringNumber
#New format: 15.0000

所以基本上我想出的是:

  • 2014 年有人说将 my 转换string为 anint不会可靠地作业,即使似乎Cast 运算子实际上正在执行转换
  • 这些ToInt32方法不喜欢以小数作为输入的字符串
  • 显然String.ToString方法没用
  • 由于复合格式String.ToString处理顺序处理顺序,我的字符串表示的简单“重新格式化”将不起作用

总结:有没有办法安全地将 my$StringNumber转换为整数,如果是,那么在大型资料集上执行此操作的最有效方法是什么?

奖励挑战: 如果有人可以使用ForEach 魔法方法完成这项作业,那么我就请你喝啤酒。这是一些不起作用的伪代码,但如果它起作用会很棒。据我所知,在设定 a 的值时无法参考集合中的当前项目string property

#This code DOES NOT work as written
PS > $CSVData = Import-Csv .\somedata.csv
PS > $CSVData.ForEach('StringNumberField', [int]$_.StringNumberField)

uj5u.com热心网友回复:

  • 如果您的字符串表示可以解释为数字,则可以将其强制转换为整数,只要所使用的特定整数型别足够大以容纳所表示的值(例如[int] '15.0000')的(整数部分

    • 一个字符串可以被解释为一个数字或表示数字,是过大(或小,对于负数),用于目标型别,导致宣告终止错误; 例如[int] 'foo'[int] '444444444444444'

    • 请注意,PowerShell 的强制转换隐式字符串到数字的转换使用不变文化,这意味着只有 ever.被识别为小数点(并且,实际上被忽略,因为它被解释为千位分组符号),与文化无关当前有效(如 所示$PSCulture)。

    • 至于您可以使用的整数型别(所有这些 - 除了开放式[bigint]型别 - 支持::MinValue::MaxValue确定它们可以容纳的整数范围;例如[int]::MaxValue

      • 有符号整数型别:[sbyte], [int16], [int]( [int32]), [long]( [int64]),[bigint]
      • 无符号整数型别:[byte], [uint16], [uint]( [uint32]), [ulong]( [uint64]) - 但请注意 PowerShell 本身在其计算中仅使用本机有符号型别。
  • 转换为整数型别执行半到偶数中点舍入,这意味着表示小数部分为的值的字符串.5被舍入到最接近的偶数整数;例如[int] '1.5'[int] '2.5' 两者都舍入到2

    • 要选择不同的中点舍入策略,请[Math]::Round()System.MidpointRounding自变量一起使用例如:

      [Math]::Round('2.5', [MidPointRounding]::AwayFromZero) # -> 3
      
    • 无条件地向上或向下舍入到最接近的整数,请使用[Math]::Ceiling()[Math]::Floor()、 或[Math]::Truncate()例如:

      [Math]::Ceiling('2.5')    # -> 3
      [Math]::Floor('2.5')      # -> 2
      [Math]::Truncate('2.5')   # -> 2
      #
      [Math]::Ceiling('-2.5')   # -> -2
      [Math]::Floor('-2.5')     # -> -3
      [Math]::Truncate('-2.5')  # -> -2
      
    • 注意:虽然结果数字在概念上是一个整数,但从技术上讲,它是 a[double]或 - 带有显式[decimal]或整数数字文字输入 - a [decimal]


至于奖金挑战

  • 使用整数型别转换:
[int[]] (Import-Csv .\somedata.csv).StringNumberField

注意:(Import-Csv .\somedata.csv).StringNumberField.ForEach([int])也可以,但在这里没有优势。

  • 通过[Math]::*()呼叫和.ForEach()阵列方法
(Import-Csv .\somedata.csv).StringNumberField.ForEach(
  { [Math]::Round($_, [MidPointRounding]::AwayFromZero) }
)

uj5u.com热心网友回复:

铸造[int]为你解释,是东西,将作业在大多数情况下,但它也容易出错。如果数字大于[int]::MaxValue怎么办?您可以用来避免例外的替代方法是使用-as [int]运算子,但是还有另一个问题,如果该值无法转换为整数,您将获得$null结果。

为了确保字符串将被转换并且您不会首先得到 null 作为结果,您需要 100% 确定您提供的资料是正确的,或者假设最坏的情况并[math]::Round(..)-as [decimal]or-as [long]-as [double]( )结合使用四舍五入你的数字:

[math]::Round('123.123' -as [decimal]) # => 123
[math]::Round('123.asd' -as [decimal]) # => 0

注意:我正在使用 round but[math]::Ceiling(..)[math]::Floor(..)or[math]::Truncate(..)也是有效的替代方案,具体取决于您的预期输出。

另一种选择是使用[decimal]::TryParse(..)但是如果有一些不是数字的东西,这会抛出:

$StringNumber = '15.0000'
$ref = 0
[decimal]::TryParse( $StringNumber, ([ref]$ref) )
[math]::Round($ref) # => 15

使用Hazrelle 的建议也可以,但同样会因无效输入或“值对于 Int32 而言太大或太小”而引发例外

[System.Decimal]::ToInt32('123123123.123') # => 123123123

至于奖金挑战,我认为不可能一次性使用 将四舍五入的值投射到您的 CSV 中ForEach(type convertToType),即使是这样,由于之前提到的内容,它也可能带来问题:

$csv = @'
"Col1","Col2"
"val1","15.0000"
"val2","20.123"
"val3","922337203685477.5807"
'@ | ConvertFrom-Csv

$csv.Col2.ForEach([int])

无法转换自变量“item”,值为“922337203685477.5807”,“添加”型别为“System.Int32”:“无法将值“922337203685477.5807”转换为型别“System.Int32”。

.foreach(..)阵列方法与脚本块结合使用会起作用:

$csv.ForEach({
    $_.Col2 = [math]::Round($_.Col2 -as [decimal])
})

如果您想知道为什么不直接使用[math]::Round(..)字符串并忘记它:

[math]::Round('123.123') # => 123 Works!

但是关于:

PS /> [math]::Round([decimal]::MaxValue -as [string])
7.92281625142643E 28

PS /> [math]::Round([decimal]([decimal]::MaxValue -as [string]))
79228162514264337593543950335
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *