如何在Powershell中将字符串转换为UInt64?字符串转数字

7

考虑以下的 Powershell 代码片段:

[Uint64] $Memory = 1GB
[string] $MemoryFromString = "1GB"
[Uint64] $ConvertedMemory = [Convert]::ToUInt64($MemoryFromString)

第三行代码执行失败,错误信息为:
Exception calling "ToUInt64" with "1" argument(s): "Input string was not in a correct format."
At line:1 char:1
+ [Uint64]$ConvertedMemory = [Convert]::ToUInt64($MemoryFromString)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : FormatException

如果我查看$Memory 的内容:
PS C:\> $Memory
1073741824

那很好。

那么,我如何在 Powershell 中将字符串“1GB”的值转换为 UInt64?

2个回答

14

为了补充Sean的帮助性答案:

只有你的结果变量的类型约束 ([uint64] $ConvertedMemory = ...) 确保 ($MemoryFromString / 1) 转换成了[uint64] ([System.UInt64])。

表达式$MemoryFromString / 1 的结果实际上是[int] ([System.Int32]) 类型的:

PS> ('1gb' / 1).GetType().FullName
System.Int32
因此,为了确保表达式本身返回一个[uint64]实例,你需要使用强制类型转换

因此,为了确保表达式本身返回一个[uint64]实例,您必须使用类型转换

PS> ([uint64] ('1gb' / 1)).GetType().FullName
System.Int64
注意计算周围的必需的(...),因为否则[uint64]转换仅适用于'1gb'(因此失败)。
另外,('1gb' / [uint64] 1)也可以工作。

注意:

  • '1gb' - 0也可以工作,
  • 但是不是 '1gb' * 1'(实际上是一个无操作)或者 '1gb' + 0(结果是字符串'1gb0'),因为带有字符串类型左值的运算符*+执行字符串操作(分别进行复制和连接)。

PowerShell中的自动字符串到数字转换数字字面量

当PowerShell执行隐式数值转换,包括在执行混合数值类型计算和解析源代码中的数字字面量时,它会方便地自动选择一个足够"大"以容纳结果的数值类型

隐式字符串到数字转换中,PowerShell方便地识别与源代码中数字字面量支持的相同格式

  • 数字-基数前缀(仅适用于整数):0x表示十六进制整数,0b表示二进制整数(PowerShell [Core] 7.0+)

  • 数字-类型后缀L表示[long][System.Int64]),D表示[decimal][System.Decimal]);例如,'1L' - 0产生一个[long]
    请注意,C#使用M代替D,并将D用于指定[System.Double];此外,C#还支持多个其他后缀。

      PowerShell [Core] 6.2+ 现在支持额外的后缀: Y ([sbyte]), UY ([byte]), S ([int16]), US ([uint16]), U ([uint32] [uint64], 根据需要), 和 UL ([uint64]).

    • PowerShell [Core] 7.0+ 还支持后缀 n ([bigint])

    • 您可以通过官方帮助主题about_Numeric_Literals,了解未来的开发。

  • 浮点数 表示,例如 1.23 (仅十进制); 请注意,无论当前区域设置如何,PowerShell只会将. 作为十进制分隔符识别。

  • 指数表示法(仅十进制); 例如,'1.0e3' - 1得到999

  • 它拥有自己的二进制乘数后缀kbmbgbtbpb(用于乘数[math]::pow(2, 10) == 1024[math]::pow(2, 20) == 1048576,...); 例如,'1kb' - 1 得到 1023; 请注意,这些后缀是PowerShell特有的,因此.NET框架的数字解析方法不会识别它们。

数字转换规则 很复杂,但以下是一些关键点

这是基于我的个人实验。如果我错了,请告诉我。
类型以其 PS 类型加速器表示,并映射到 .NET 类型如下:
[int] ... [System.Int32]
[long] ... [System.Int64]
[decimal] ... [System.Decimal]
[float] ... [System.Single]
[double] ... [System.Double]

  • PowerShell 从不自动选择 无符号 整数类型

    • 注意:在 PowerShell [Core] 6.2+ 中,您可以使用类型后缀 USUUL(请参见上文)强制将其视为无符号类型(正数);例如,0xffffffffffffffffU
    • 这可能会令人意外的是十六进制的 数字字面量;例如:[uint32] 0xffffffff 失败,因为 0xffffffff 被隐式地转换为 有符号 类型 [int32],导致结果为 -1,作为一个 有符号 值,无法转换为 无符号 类型 [uint32]
    • 解决方法:
      • 追加 L 强制将其解释为 [int64],这将得到预期的正值 4294967295,此时转换为 [uint32] 就会成功。
      • 然而,对于超过 0x7fffffffffffffff[long]::maxvalue)的值,该技术不起作用,在这种情况下,可以使用 字符串 转换:[uint64] '0xffffffffffffffff'
  • PowerShell 扩展 整数类型以满足需要

    • 对于十进制整数字面量/字符串,扩展超出了整数类型,直到达到[System.Decimal],然后是[Double],如有需要;例如:

      • (2147483648).GetType().Name 得到 Int64,因为该值为 [int32]::MaxValue + 1,因此被隐式地扩展为 [int64]

      • (9223372036854775808).GetType().Name的结果为Decimal,因为该值是[int64]::MaxValue + 1,因此被隐式扩展为[decimal]

        (79228162514264337593543950336).GetType().Name的结果为Double,因为该值是[decimal]::MaxValue + 1,因此被隐式扩展为[double]

        对于十六进制(必然是整数)字面量/字符串,扩展到[int64]后停止

        (0x100000000).gettype().name的结果为Int64,因为该值是[int32]::MaxValue + 1,因此被隐式扩展为[int64]

        0x10000000000000000,即[int64] :: MaxValue + 1,由于是十六进制,解释为数字因此失败,不会被升级为[System.Decimal]

        注意:上述规则适用于单个字面量/字符串,但在表达式中进行扩展可能会直接扩展到[double](而不考虑[decimal])-见下文。

        PowerShell似乎从不自动选择比[int]小的整数类型

        ('1' - 0).GetType().FullName返回System.Int32(一个[int]),即使整数1可以适合[int16]甚至[byte]

        计算结果从不使用比任何操作数更小的类型:

        1 + [long] 1[long] 1 + 1都会产生[long] (即使结果可以适合更小的类型)。

        也许出乎意料的是,当计算结果大于任一操作数的整数类型范围时,PowerShell自动选择浮点类型[double],即使结果可以适合更大的整数类型:

        • ([int]::maxvalue + 1).GetType().FullName 输出 System.Double(一个[double]),即使结果可以适合[long]整数。
        • 但是,如果操作数中的一个是足够大的整数类型,则结果为该类型:([int]::maxvalue + [long] 1).GetType().FullName 输出System.Int64(一个[long])。
      • 涉及至少一个浮点类型的计算始终会产生[double]结果,即使混合了整数类型或使用所有[float]操作数:

        • 1 / 1.01.0 / 11 / [float] 1[float] 1 / 1[float] 1 / [float] 1都将产生[double]
      • 源代码中没有使用类型后缀的数字文字:

        • 十进制整数字面量被解释为以下可以容纳该值的最小类型:[int] > [long] > [decimal] > [double](!)

          • 1会产生一个[int](如上所述,[int]是最小的自动选择类型)
          • 214748364(比[int] :: maxvalue 高1)会产生一个[long]
          • 9223372036854775808(比[long]::maxvalue高1)会产生一个[decimal]
          • 79228162514264337593543950336(比[decimal]::maxvalue高1)会产生一个[double]
        • 十六进制整数字面量被解释为以下可以容纳该值的最小类型:[int] > [long];也就是说,与十进制文字不同,不支持大于[long]的类型;注意:因为PowerShell自动选择有符号整数类型,具有高位设置的值导致负十进制数

          • 0x1产生一个[int]

            0x80000000产生一个[int],它是一个负数,因为高位被设置了:-2147483648,也是最小的[int]数字,如果考虑符号([int]::MinValue)

            0x100000000(比一个[int](或[uint32])能容纳的多1)产生一个[long]

            0x10000000000000000(比一个[long](或[uint64])能够容纳的多一个)报错,因为[long]是支持的最大类型(“数字常量无效”)。

            为确保十六进制文字结果为正数

            • Windows PowerShell:使用类型后缀L强制解释为[long],然后(可选)转换为无符号类型;例如,[uint32] 0x80000000L产生2147483648,但请注意,此技术仅适用于0x7fffffffffffffff,即[long]::maxvalue;如上所述,使用从字符串转换作为解决方法(例如,[uint64] '0xffffffffffffffff')。
            • PowerShell [Core] 6.2+:根据需要使用类型后缀usuul;例如:0x8000us->32768[uint16])、0x80000000u->2147483648[uint32])、0x8000000000000000ul->9223372036854775808[uint64]
          • 二进制整数字面量(PowerShell [Core] 7.0+)的解释方式与十六进制相同; 例如,0b10000000000000000000000000000000 == 0x80000000 == -2147483648[int]

            浮点指数表示法字面量(仅在十进制中识别)始终被解释为[double],无论多么小:

            • 1.01e0都产生[double]

2
非常好的答案,为我解决了很多问题,感谢您抽出时间来整理这个。 - Mark Allison

10

你的问题在于ToUint64不理解PowerShell语法。你可以通过以下方式解决:

($MemoryFromString / 1GB) * 1GB

$MemoryFromString在进行除法运算之前会被转换成相应的数值。

这是有效的,因为在除法运算时,PowerShell会尝试使用自己的规则将字符串转换为数字,而不是使用内置于ToUInt64中的 .Net 规则。在转换过程中,如果发现字符串中含有GB后缀,则会使用其规则将"1GB"字符串扩展为1073741824

编辑:或者如PetSerAl所指出的那样,您也可以直接执行:

($MemoryFromString / 1)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接