如果事先不知道数据类型,是否可以将字符串值分配给不同类型的变量?例如,在下面的示例中,如何在不更改其数据类型的情况下更新$values
哈希值:
$values = @{
"Boolean" = $true
"Int" = 5
"DateTime"= (Get-Date)
"Array" = @("A", "B", "C")
}
$stringValues = @{
"Boolean" = 'false'
"Int" = '10'
"DateTime"= '2019-01-02 14:45:59.146'
"Array" = '@("X", "Y", "Z")'
}
"INITIAL VALUES:"
foreach ($key in $values.Keys) {
($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}
"`nUPDATING..."
foreach ($key in $stringValues.Keys) {
$values[$key] = $stringValues[$key]
}
"`nUPDATED VALUES:"
foreach ($key in $values.Keys) {
($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}
OUTPUT:
INITIAL VALUES:
DateTime = 04/23/2019 16:54:13 (System.DateTime)
Array = A B C (System.Object[])
Boolean = True (System.Boolean)
Int = 5 (System.Int32)
UPDATING...
UPDATED VALUES:
DateTime = 2019-01-02 14:45:59.146 (System.String)
Array = @("X", "Y", "Z") (System.String)
Boolean = false (System.String)
Int = 10 (System.String)
我需要更新的值来匹配原始数据类型,而不仅仅是转换为System.String
。
我对字符串的内容很灵活。例如。保存布尔值false
值的字符串可能是$false
/ false
/ [boolean]false
/ [boolean]$false
/ etc,或者持有数组的字符串可能使用不同的格式(基本上,更容易将字符串转换为正确的数据类型)。
本质上,我想模拟ConvertFrom-Json
cmdlet在从JSON字符串设置对象属性时所执行的操作,仅在我的情况下,我没有JSON结构。
(如果有人想知道我正在尝试做什么:我试图将一个INI文件解析器添加到我的ConfigFile module,不,我不能只使用哈希来返回INI设置;我需要将值加载到相应的PSVariable
s并为此工作,我需要将字符串转换为正确的数据类型。)
您可以使用自定义类代替哈希表;与散列表键不同,可以专门键入自定义类的属性。
[System.Management.Automation.PSParser]::Tokenize()
来解析字符串,但目前仅限于识别字符串和数字文字。
注意:在整个阵列上使用Invoke-Expression
很诱人,但这会带来安全风险,因为它打开了通向任意代码的大门。虽然有合法的用途 - 例如在已知代表下面数字的字符串上 - Invoke-Expression
should generally be avoided。(如果您不想定义类,可以从散列表$values
的值派生类型,并使用[System.Management.Automation.LanguagePrimitives]::ConvertTo()
将字符串转换为这些类型,如LotPings' answer所示,但请注意,数组和布尔值仍然需要特殊处理,如图所示下面。)
# Define a custom [Values] class
# with specifically typed properties.
class Values {
[bool] $Boolean
[int] $Int
[datetime] $DateTime
[Array] $Array
}
# Instantiate a [Values] instance
$values = [Values] @{
Boolean = $true
Int = 5
DateTime= (Get-Date)
Array = @("A", "B", "C")
}
$stringValues = @{
Boolean = 'false'
Int = '10'
DateTime = '2019-01-02 14:45:59.146'
Array = '@("X", "Y", "Z")'
}
"INITIAL VALUES:"
foreach ($key in $values.psobject.properties.Name) {
($key + " = " + $values.$key + " (" + $values.$key.GetType().FullName + ")")
}
""
"UPDATING..."
foreach ($key in $stringValues.Keys) {
switch ($key) {
'Array' {
# Parse the string representation.
# Assumptions and limitations:
# The array is flat.
# It is sufficient to only support string and numeric constants.
# No true syntax validation is needed.
$values.$key = switch ([System.Management.Automation.PSParser]::Tokenize($stringValues[$key], [ref] $null).Where( { $_.Type -in 'String', 'Number' })) {
{ $_.Type -eq 'String' } { $_.Content; continue }
{ $_.Type -eq 'Number' } { Invoke-Expression $_Content; continue }
}
continue
}
'Boolean' { # Boolean scalar
# Boolean strings need special treatment, because PowerShell considers
# any nonempty string $true
$values.$key = $stringValues[$key] -notin 'false', '$false'
continue
}
default { # Non-Boolean scalar
# Let PowerShell perform automatic from-string conversion
# based on the type of the [Values] class' target property.
$values.$key = $stringValues[$key]
}
}
}
""
"UPDATED VALUES:"
foreach ($key in $values.psobject.properties.Name) {
($key + " = " + $values.$key + " (" + $values.$key.GetType().FullName + ")")
}
这会产生:
INITIAL VALUES:
Boolean = True (System.Boolean)
Int = 5 (System.Int32)
DateTime = 04/24/2019 14:45:29 (System.DateTime)
Array = A B C (System.Object[])
UPDATING...
UPDATED VALUES:
Boolean = True (System.Boolean)
Int = 10 (System.Int32)
DateTime = 01/02/2019 14:45:59 (System.DateTime)
Array = X Y Z (System.Object[])
因此,您希望将新值转换/转换为旧值的类型。
这个想法需要从变量中转换出来, 这是一个相关的问题powershell-type-cast-using-type-stored-in-variable
答案表明:
您可以使用以下方法粗略地模拟强制转换:[System.Management.Automation.LanguagePrimitives] :: ConvertTo($ Value,$ TargetType)
以下更改的例程显示:它并不那么简单,尤其是当新数据需要转换中的重载/其他参数时。
"UPDATING..."
foreach ($key in $stringValues.Keys) {
$values[$key] = [System.Management.Automation.LanguagePrimitives]::ConvertTo(
$stringValues[$key], $values[$key].gettype())
}
我的德语区域设置错误消息:
使用2个参数调用“ConvertTo”时出现异常:“值”2019-01-02 14:45.59.146“无法转换为”System.DateTime“类型错误:”该字符串未用作有效的DateTime被识别。“”在行:2个字符:5 + $ values [$ key] = [System.Management.Automation.LanguagePrimitives] ... + ~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CategoryInfo:NotSpecified:(:) [],MethodInvocationException + FullyQualifiedErrorId:PSInvalidCastException
并且效果不佳:
DateTime = 04/24/2019 09:49:19 (System.DateTime)
Array = @("X", "Y", "Z") (System.Object[])
Boolean = True (System.Boolean)
Int = 10 (System.Int32)
您可以详细说明这个想法,更单独地处理旧类型/新数据。
同意Write-Host的事情。它应该只用于利用颜色输出和一些特定的格式情况。输出到屏幕是默认设置,您将在我的回复中看到。
你可以在下面做,但那个日期字符串有点奇怪,好吧,对我来说,好吧,我没见过有人使用那种格式。因此,我将其修改为美国风格,但根据您的语言需要进行更改。
$values = @{
'Boolean' = $true
'Int' = 5
'DateTime'= (Get-Date)
'Array' = @('A', 'B', 'C')
}
$stringValues = @{
'Boolean' = 'false'
'Int' = '10'
'DateTime'= '2019-01-02 14:45:59'
'Array' = "@('X', 'Y', 'Z')"
}
'INITIAL VALUES:'
foreach ($key in $values.Keys)
{
"$key = $($values[$key]) $($values[$key].GetType())"
}
"`nUPDATING..."
foreach ($key in $stringValues.Keys)
{
switch ($key)
{
Boolean {[Boolean]$values[$key] = $stringValues['$'+$key]}
Int {[Int]$values[$key] = $stringValues[$key]}
DateTime {[DateTime]$values[$key] = $stringValues[$key]}
Array {[Array]$values[$key] = $stringValues[$key]}
default {'The value could not be determined.'}
}
}
"`nUPDATED VALUES:"
foreach ($key in $values.Keys)
{
"$key = $($values[$key]) $($values[$key].GetType())"
}
# Results
INITIAL VALUES:
DateTime = 04/24/2019 01:44:17 datetime
Array = A B C System.Object[]
Boolean = True bool
Int = 5 int
UPDATING...
UPDATED VALUES:
DateTime = 01/02/2019 14:45:59 datetime
Array = @("X", "Y", "Z") System.Object[]
Boolean = False bool
Int = 10 int
感谢@LotPings,@ mklement0和@postanote为我提供了一些想法,所以这里是我将采用的解决方案:
$values = @{
"Boolean" = $true
"Int" = 5
"DateTime"= (Get-Date)
"Array" = @("A", "B", "C")
}
$stringValues = @{
"Boolean" = 'false'
"Int" = '10'
"DateTime"= '2019-01-31 14:45:59.005'
"Array" = 'X,Y,Z'
}
"INITIAL VALUES:"
foreach ($key in $values.Keys) {
($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}
"`nUPDATING..."
foreach ($key in $stringValues.Keys) {
$value = $stringValues[$key]
if ($values[$key] -is [Array]) {
$values[$key] = $value -split ','
}
elseif (($values[$key] -is [Boolean]) -or ($values[$key] -is [Switch])) {
$values[$key] = $value -notin 'false', '$false', '0', ''
}
else {
$values[$key] = [System.Management.Automation.LanguagePrimitives]::ConvertTo($value, $values[$key].GetType())
}
}
"`nUPDATED VALUES:"
foreach ($key in $values.Keys) {
($key + " = " + $values[$key] + " (" + $values[$key].GetType().FullName + ")")
}
OUTPUT:
INITIAL VALUES:
DateTime = 04/25/2019 09:32:31 (System.DateTime)
Array = A B C (System.Object[])
Boolean = True (System.Boolean)
Int = 5 (System.Int32)
UPDATING...
UPDATED VALUES:
DateTime = 01/31/2019 14:45:59 (System.DateTime)
Array = X Y Z (System.String[])
Boolean = False (System.Boolean)
Int = 10 (System.Int32)
我在字符串值中调整了数组的格式(正如我在问题中提到的那样,这是一个选项)。使用它的实际代码会有所不同,但基本思路就在这里。我注意到的唯一警告是数组数据类型从object[]
更改为string[]
。理想情况下,我希望保持原样,但它不会改变代码的功能,所以很好。再次感谢所有想法和更正,如果你想出更好的选择,请随时发布。