数字在 powershell 的变量内部变化

Numbers change inside variables for powershell

我只是试图将包含星期和日期信息的字符串转换为数字并将其存储为变量,但确实发生了一些奇怪的事情,截至目前,我已经在 4 台 PC 和 Powershell 中测试了这种行为5 和 7 无处不在。

$UP_Down = "6w0d"

[int]$weeks = if ($Up_Down -match "w"){$Up_Down[$($Up_Down.IndexOf('w')-1)]}Else{0}

[int]$days = if ($Up_Down -match "d"){$Up_Down[$Up_Down.IndexOf('d')-1]}Else{0}

[int]$totaldays = (7 * $weeks) + $days

现在初始变量的数据显然是 6 周0 天,我必须将其转换为 42 总天数(这只是一个例子,它的发生与组合无关)

然而,以下是我得到的 Funky 结果,由 Write-Output

详细阐述
Weeks If statement results by itself 6
Weeks variable results 54
days If statement results by itself 0
days variable results  48
totaldays variable results are 426

无论我使用什么数字数据类型都会出现这个问题

具有讽刺意味的是,如果我不为它们分配数据类型,变量具有正确的值,但是

它命中 (7*$weeks) 的那一刻,即使 $weeks 是正确的输出值 426,记住任何地方都没有 [int]etc

我做错了什么?

问题是,您没有将字符串 "6" 中的数字转换为数字 6,而是根据底层字符编码方案 54 将字符 '6' 转换为它的值在 ASCII 的情况下。与天相同:'0' 的值为 48。7 * 54 + 48 = 426.

看区别:

PS C:\Users\name> [int]"6"[0]
54
PS C:\Users\name> [int]"6"
6

当通过使用 [0] 进行索引提取字符串的元素时,您会得到一个字符而不是长度为 1 的字符串。转换为 int 将 return ASCII 值这个角色。

您正在 索引 ([...]) 到字符串 $Up_Down,这意味着 您正在返回一个 单个字符,即一个[char] (System.Char)实例.

[char] 转换为 [int] 会产生其 Unicode 代码点 ("ASCII value") ,不是该字符恰好代表的数字。

例如,字符 6是代码点为U+0036的Unicode字符DIGIT SIX; 0036是数字码位的十六进制形式,十六进制0x36的十进制形式是54.

PS> [int] "6w0d"[0]
54 # !! Same as: [int] [char] "6"

要将字符解释为 数字您需要中级 [string] 转换:

PS> [int] [string] "6w0d"[0]
6   # OK - a string is parsed as expected; same as: [int] "6"

如果将 字符串 而不是 char 转换为 [int],PowerShell 会在幕后有效地调用 System.Int32.Parse,如下所示:[int]::Parse($string, [cultureinfo]::InvariantCulture).

注意 PowerShell no char literals - 与 C# 不同, '...' 引用 also 产生 strings (verbatim ones), [int] '6' 产生整数 6, 只是就像 [int] "6" 那样。

相反,您需要显式 [char] 转换才能将单字符字符串文字转换为 [char];例如,[char] '6';多字符字符串会导致转换 失败 .


在您的命令上下文中的解决方案:

[int]$weeks = if ($Up_Down -match "w"){[string] $Up_Down[$Up_Down.IndexOf('w')-1]} Else {0}

[int]$days = if ($Up_Down -match "d"){[string] $Up_Down[$Up_Down.IndexOf('d')-1]} Else {0}

但是,我建议以不同的方式解决问题:

[int] $totalDays = 0
if ($UP_Down -match '^(?:(?<weeks>\d+)w)?(?:(?<days>\d+)d)?$') {
  [int] $weeks, [int] $days = $Matches.weeks, $Matches.days
  $totalDays = 7 * $weeks + $days
} # else: string wasn't in expected format.

其他人已经向您展示了为什么遇到这个问题,所以这只是获取总天数的另一种方法。 [咧嘴一笑]

它的作用...

  • 在 Week/Day 个代码的文本文件中伪造读取
    当准备使用真实数据时,删除整个 #region/#endregion 块并使用 Get-Content
  • 遍历列表
  • 分裂 w
  • 修剪掉尾随 d
  • 将结果字符串分配给 =
    左侧的两个 [int] 变量 这迫使两个数字 strings 变成数字 objects
  • 计算总天数
  • 显示 week/day 代码、周数、天数和总天数

代码...

#region >>> fake reading in a list of week/day codes
#    in real life use Get-Content
$WD_List = @'
6w0d
3w3d
0w1d
66w6d
9w1d
'@ -split [System.Environment]::NewLine
#endregion >>> fake reading in a list of week/day codes

foreach ($WL_Item in $WD_List)
    {
    [int]$WeekCount, [int]$DayCount = $WL_Item.Split('w').TrimEnd('d')
    $TotalDays = ($WeekCount * 7) + $DayCount


    $WL_Item
    $WeekCount
    $DayCount
    $TotalDays
    '=' * 20
    }

输出...

6w0d
6
0
42
====================
3w3d
3
3
24
====================
0w1d
0
1
1
====================
66w6d
66
6
468
====================
9w1d
9
1
64
====================