将 System.UInt16 数组转换为字符串

Converting a System.UInt16 array to a string

我遇到了一个奇怪的问题,我需要删除可能不是 space 或标签的 "blank characters"。

这是来自WMI的信息。 "SerialNumberID" 属性 存储为 UInt16[] 数组。

我是这样收集的:

$SERIAL = (Get-WmiObject -ComputerName XXXX WmiMonitorID -Namespace root\wmi ).SerialNumberID

现在包含以下数组:

72
86
76
81
66
48
50
53
52
50
0
0
0
0
0
0

要将其转换为可读格式,我正在做:

$SERIAL_AS_STRING = [System.Text.Encoding]::Default.GetString($SERIAL)

"+$SERIAL_AS_STRING+"

(我用+号方便识别数据后是否有字符)

这个returns:

+HVLQB02542      +

我只想得到序列号,所以我想像这样去掉 spaces:

$SERIAL_AS_STRING = $SERIAL_AS_STRING.Trim()

"+$SERIAL_AS_STRING+"

它仍然 returns 有空白 spaces! :

+HVLQB02542      +

即使我尝试类似的方法:

$SERIAL_AS_STRING = $SERIAL_AS_STRING -replace '(^\s+|\s+$)','' -replace '\s+',' '

space总是在那里。

现在我看到在 System.UInt16 数组中我有六个尾随零,这恰好与我结果字符串中的六个尾随 spaces 相符....

所以我尝试了一种不同的方法来转换数组,即:

$SERIAL2 = ($SERIAL -notmatch 0 | ForEach{[char]$_}) -join ""

"+$SERIAL2+"

这可以删除尾随的 spaces 但是这是不可接受的,因为它会像这样弄乱序列:

+HVLQB054+

应该是:+HVLQB02542+

我在这里做错了什么?很奇怪,如果我特别要求更换六个 spaces,它也不起作用。这就是为什么我怀疑这些 "spaces" 不是真正的 space 字符!

目前,我使用 RegEx 使其工作,它会删除所有非字母或数字的内容:

$FINAL = $SERIAL_AS_STRING -replace "[^A-Za-z0-9]"

但是如何正确完成它,因为这感觉像是一种变通解决方案?

编辑:在下面用户 TrebleCode 的帮助下,我能够将违规字符识别为像这样的 Ascii 代码 0 字符 [byte][char]$SERIAL_AS_STRING[16]。这返回了 0,所以现在我知道我必须用任何东西替换 Ascii 代码 0。

我是这样解决的:

$ascii0 = [char]0
$FinalString = $SERIAL_AS_STRING -replace $ascii0,""


"+$FinalString+"

+HVLQB02542+

啊,没有更多的垃圾空白字符:) ASCII 代码 0 实际上是 0 NUL(空),而代码 32 是 Space.

您可以通过转换 space 字符之一的类型并检查 ASCII 代码来检查它们是否实际上是 space:

$a = '+HVLQB02542      +'

获取倒数第二个字符并检查它的 return 值:

[byte][char]$a[16]

which returns 32,这是一个space

的ASCII码

使用 -replace 运算符的另一种方法,如果去掉最后两个单引号之间的 space,您应该得到所需的结果:

$a -replace '(^\s+|\s+$)','' -replace '\s+',''

return秒:+HVLQB02542+

你也可以这样做 $a.replace(' ','') 并且应该产生与上面相同的结果

虽然 $a 最初将 return 作为 UInt16[]

,但我在尝试执行 [System.Text.Encoding]::Default.GetString($a) 时遇到错误

.SerialNumberID 输出的是一个 int [uint16[]] 数组(参见 the docs)。

如果您想将这些 uint16 实例转换为 字符串,您可以简单地将它们 直接 解释为 Unicode UTF -16 代码点(Unicode 等同于 ASCII 码,它是超集),这就是字符 ([char]) 实例在 .NET 内存中的表示方式.

因此,您可以 简单地转换为 [char[]] 并加入 -join 结果字符数组以形成 [string].

(也就是说,不需要 [System.Text.Encoding]::Default.GetString(),顺便说一句,会将这些值解释为 字节 .[1])

您的 .SerialNumberID 有尾随 0 个实例,但是 [char[]][System.Text.Encoding]::Default.GetString() 在转换中保留 ,作为 NUL 个字符。

您可以 删除不需要的 0 / NUL 实例,只需在转换前使用 -ne 0 从输入数组中过滤掉它们.

总而言之:

# Simulate the result of your (Get-WmiObject ...).SerialNumberID call
$SERIAL = [uint16[]] (72, 86, 76, 81, 66, 48, 50, 53, 52, 50, 0, 0, 0, 0, 0, 0)

$SERIAL_AS_STRING = -join [char[]] ($SERIAL -ne 0)

# Show the resulting string
"+$SERIAL_AS_STRING+"

以上结果 +HVLQB02542+,如您所愿。


[1] 只有当输入数组表示 UTF-16 以外的特定字符编码时,您才需要适当的 [System.Text.Encoding] 实例的 .GetString() 方法;请注意,该方法需要一个 [byte[]] 数组,虽然 PowerShell 将尝试自动将 [uint16[]] 强制转换为该数组,但如果任何数组元素,这样做将 失败 具有大于 255 (0xFF) 的值,即太大而无法放入 [byte].

类似的说明...将 System.UInt16[] 数组转换为整数示例:
$rv=(Get-CIMInstance Win32_SystemEnclosure).ChassisTypes; [int]$as= -join [int[]] ($rv -ne 0);