如果事先不知道数据类型,是否可以将字符串值分配给其他类型的变量?例如,在下面的示例中,如何在不更改其数据类型的情况下更新$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 + ")")
}
输出:
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结构。
(以防万一有人想知道我要做什么:我正在尝试向我的ConfigFile module添加一个INI文件解析器,不,我不能仅使用哈希返回INI设置;我需要要将值加载到相应的PSVariable
中,并且要使其正常工作,我需要将字符串转换为适当的数据类型。)
答案 0 :(得分:2)
您可以使用自定义类代替哈希表;与哈希表键不同,自定义类的属性可以专门键入。
使用标量值,然后您可以简单地让PowerShell为您执行从字符串转换-除了布尔字符串需要特殊处理(请参见下面的源代码中的注释)。
使用数组,事情变得更加棘手。下面的解决方案使用[System.Management.Automation.PSParser]::Tokenize()
来解析字符串,但目前仅限于识别字符串和数字文字。
Invoke-Expression
是很诱人的,但这将带来安全风险,因为它为任意代码执行打开了方便之门。尽管存在合法用途-例如在已知表示以下数字的字符串上-Invoke-Expression
should generally be avoided。(如果不想定义类,则可以从哈希表$values
的 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[])
答案 1 :(得分:1)
在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
答案 2 :(得分:1)
因此您想将新值转换/转换为旧值的类型。
这个想法需要从变量中转换出来,
这是一个相关的问题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())
}
我的德语语言环境错误消息:
Ausnahme beim Aufrufen von“ ConvertTo” mit 2自变量:“ Der Wert” 2019-01-02 14:45.59.146“ kann nicht in den Typ “ System.DateTime”显示广告。费勒:“死去的日期时间不正确。” 在Zeile:2 Zeichen:5 + $ values [$ key] = [System.Management.Automation.LanguagePrimitives] ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~ + CategoryInfo:未指定:(:) [],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)
您可以详细说明这个想法,更单独地处理旧类型/新数据。
答案 3 :(得分:0)
感谢@ 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 + ")")
}
输出:
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[]
。理想情况下,我希望保持原样,但它不会更改代码的功能,因此很好。再次感谢所有人的想法和纠正,如果您提出更好的选择,请随时发帖。