【问题标题】:How can I extract the values from this Hashtable-like text in Powershell?如何在 Powershell 中从这个类似 Hashtable 的文本中提取值?
【发布时间】:2020-03-10 19:56:43
【问题描述】:

我有一个String,格式如下,需要帮助将其转换为可以轻松访问内部键值对的数据结构。

@{7068="@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}";7602="@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}";...}

我曾尝试使用ConvertFrom-String,但是我无法正确操作String 以满足这种格式。

【问题讨论】:

  • 你有键但并不总是值。在缺少/未指定值的情况下会发生什么?正是出于这个原因,Invoke-ExpressionHashTable-as-text 上失败了。您希望将此字符串数据映射到什么数据结构?值的可能数据类型是什么?值中是否可以出现空格或分号,或者它们是严格的分隔符?
  • 这当然是一个挑战。哈希图最适合我的需要。每个值都可以被视为一个字符串,空值是空字符串,分号是分隔符,NewValue键值对中可以出现空格。
  • 我用几种方法更新了我的答案,将这种格式的文本转换为 Hashtable 实例。两者都适用于您的样本数据以及更多测试用例。

标签: powershell parsing hashmap hashtable string-conversion


【解决方案1】:

鉴于此输入...

$testRecords = [Ordered] @{
       0  = "@{}";                            # No values
       1  = "@{DekId=1}";                     # Single value
       2  = "@{DekId=1+1=2}"                  # Single value with equal sign
      10  = "@{ }";                           # No values (with padding)
      11  = "@{ DekId=1 }";                   # Single value (with padding)
      12  = "@{ DekId=1+1=2 }"                # Single value with equal sign (with padding)
                                              # +------------------+--------------------+----------------+
                                              # | Separating space | Trailing semicolon | Trailing space |
                                              # +------------------+--------------------+----------------+
      100 = "@{First=A B C;Second=X Y Z}";    # |        No        |         No         |       No       |
      101 = "@{First=A B C;Second=X Y Z }";   # |        No        |         No         |       Yes      |
      102 = "@{First=A B C;Second=X Y Z;}";   # |        No        |         Yes        |       No       |
      103 = "@{First=A B C;Second=X Y Z; }";  # |        No        |         Yes        |       Yes      |
      104 = "@{First=A B C; Second=X Y Z}";   # |        Yes       |         No         |       No       |
      105 = "@{First=A B C; Second=X Y Z }";  # |        Yes       |         No         |       Yes      |
      106 = "@{First=A B C; Second=X Y Z;}";  # |        Yes       |         Yes        |       No       |
      107 = "@{First=A B C; Second=X Y Z; }"; # |        Yes       |         Yes        |       Yes      |
      # First property empty                  # +------------------+--------------------+----------------+
      200 = "@{First=;Second=X Y Z}";         # |        No        |         No         |       No       |
      201 = "@{First=;Second=X Y Z }";        # |        No        |         No         |       Yes      |
      202 = "@{First=;Second=X Y Z;}";        # |        No        |         Yes        |       No       |
      203 = "@{First=;Second=X Y Z; }";       # |        No        |         Yes        |       Yes      |
      204 = "@{First=; Second=X Y Z}";        # |        Yes       |         No         |       No       |
      205 = "@{First=; Second=X Y Z }";       # |        Yes       |         No         |       Yes      |
      206 = "@{First=; Second=X Y Z;}";       # |        Yes       |         Yes        |       No       |
      207 = "@{First=; Second=X Y Z; }";      # |        Yes       |         Yes        |       Yes      |
      # Second property empty                 # +------------------+--------------------+----------------+
      300 = "@{First=A B C;Second=}";         # |        No        |         No         |       No       |
      301 = "@{First=A B C;Second= }";        # |        No        |         No         |       Yes      |
      302 = "@{First=A B C;Second=;}";        # |        No        |         Yes        |       No       |
      303 = "@{First=A B C;Second=; }";       # |        No        |         Yes        |       Yes      |
      304 = "@{First=A B C; Second=}";        # |        Yes       |         No         |       No       |
      305 = "@{First=A B C; Second= }";       # |        Yes       |         No         |       Yes      |
      306 = "@{First=A B C; Second=;}";       # |        Yes       |         Yes        |       No       |
      307 = "@{First=A B C; Second=; }";      # |        Yes       |         Yes        |       Yes      |
                                              # +------------------+--------------------+----------------+
     7068 = "@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}";
     7602 = "@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}";
}

...下面使用正则表达式提取周围的@{ },然后进行字符串拆分,将里面的内容解析为[Ordered] hashtable实例...

foreach ($pair in $testRecords.GetEnumerator())
{
    Write-Host '=================================================='

    if ($pair.Value -notmatch '@{\s*(?<Body>.*)\s*}')
    {
        Write-Warning "Pattern failed to match input ""$($pair.Value)""."
    }
    else
    {
        $properties = [Ordered] @{}
        $bodyText = $Matches['Body']

        if (-not [String]::IsNullOrWhiteSpace($bodyText))
        {
            foreach ($propertyText in $bodyText -split ';\s*')
            {
                # In case the property value contains an equal sign, split
                # on only the first =, producing a two-element array
                $propertyName, $propertyValue = $propertyText -split '=', 2
                if (-not [String]::IsNullOrEmpty($propertyName))
                {
                    $properties[$propertyName] = $propertyValue
                }
            }
        }
        Write-Host "Parsed input ""$($pair.Value)"" to $($properties.GetType().Name) with Count = $($properties.Count)"
        $properties.GetEnumerator() `
            | Select-Object -Property `
                'Name', `
                'Value', `
                @{
                    Name = 'PrintableValue';
                    Expression = {
                        return $(
                            if ($_.Value -eq $null) {
                                '<null>'
                            } elseif ($_.Value.Length -eq 0) {
                                '<empty>'
                            } else {
                                $_.Value -replace '\s', [Char] 0x00B7 # Middle dot
                            }
                        )
                    };
                } `
            | Out-Host
    }
}

产生以下输出...

==================================================
Parsed input "@{}" to OrderedDictionary with Count = 0
==================================================
Parsed input "@{DekId=1}" to OrderedDictionary with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1     1


==================================================
Parsed input "@{DekId=1+1=2}" to OrderedDictionary with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1+1=2 1+1=2


==================================================
Parsed input "@{ }" to OrderedDictionary with Count = 0
==================================================
Parsed input "@{ DekId=1 }" to OrderedDictionary with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1     1·


==================================================
Parsed input "@{ DekId=1+1=2 }" to OrderedDictionary with Count = 1

Name  Value  PrintableValue
----  -----  --------------
DekId 1+1=2  1+1=2·


==================================================
Parsed input "@{First=A B C;Second=X Y Z}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=A B C;Second=X Y Z }" to OrderedDictionary with Count = 2

Name   Value  PrintableValue
----   -----  --------------
First  A B C  A·B·C
Second X Y Z  X·Y·Z·


==================================================
Parsed input "@{First=A B C;Second=X Y Z;}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=A B C;Second=X Y Z; }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=A B C; Second=X Y Z}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=A B C; Second=X Y Z }" to OrderedDictionary with Count = 2

Name   Value  PrintableValue
----   -----  --------------
First  A B C  A·B·C
Second X Y Z  X·Y·Z·


==================================================
Parsed input "@{First=A B C; Second=X Y Z;}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=A B C; Second=X Y Z; }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=;Second=X Y Z}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First        <empty>
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=;Second=X Y Z }" to OrderedDictionary with Count = 2

Name   Value  PrintableValue
----   -----  --------------
First         <empty>
Second X Y Z  X·Y·Z·


==================================================
Parsed input "@{First=;Second=X Y Z;}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First        <empty>
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=;Second=X Y Z; }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First        <empty>
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=; Second=X Y Z}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First        <empty>
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=; Second=X Y Z }" to OrderedDictionary with Count = 2

Name   Value  PrintableValue
----   -----  --------------
First         <empty>
Second X Y Z  X·Y·Z·


==================================================
Parsed input "@{First=; Second=X Y Z;}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First        <empty>
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=; Second=X Y Z; }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First        <empty>
Second X Y Z X·Y·Z


==================================================
Parsed input "@{First=A B C;Second=}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       <empty>


==================================================
Parsed input "@{First=A B C;Second= }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       ·


==================================================
Parsed input "@{First=A B C;Second=;}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       <empty>


==================================================
Parsed input "@{First=A B C;Second=; }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       <empty>


==================================================
Parsed input "@{First=A B C; Second=}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       <empty>


==================================================
Parsed input "@{First=A B C; Second= }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       ·


==================================================
Parsed input "@{First=A B C; Second=;}" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       <empty>


==================================================
Parsed input "@{First=A B C; Second=; }" to OrderedDictionary with Count = 2

Name   Value PrintableValue
----   ----- --------------
First  A B C A·B·C
Second       <empty>


==================================================
Parsed input "@{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}" to OrderedDictionary with Count = 4

Name          Value PrintableValue
----          ----- --------------
DekId               <empty>
FieldId       1234  1234
OriginalValue       <empty>
NewValue      1234  1234


==================================================
Parsed input "@{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}" to OrderedDictionary with Count = 4

Name          Value             PrintableValue
----          -----             --------------
DekId                           <empty>
FieldId       7602              7602
OriginalValue                   <empty>
NewValue      Alice, Hamburgler Alice,·Hamburgler


请注意,由于Body 组中使用了greedy quantifier(即(?&lt;Body&gt;.*)),在最后一个属性有尾随空格但没有尾随分号的情况下,该空格将包含在属性值中。如果该行为不受欢迎,您可以将其更改为惰性量词(即(?&lt;Body&gt;.*?))。

我将所有内容解析为有序的哈希表/字典,以便更轻松地将输入文本与输出属性匹配,但您也可以使用常规 Hashtable


或者,您可以使用 -replace operator 通过在匹配的属性值周围添加引号来将输入文本转换为有效的 PowerShell Hashtable syntax...

# Match the shortest text possible between "Name=" and a ";" or
# a "}" and replace it with that same text surrounded by quotes
$replacementText = $originalText -replace '(?<=[a-z]+=)(?<Value>.*?)(?=;|\s*})', '"${Value}"'

...然后使用Invoke-Expression cmdlet 将其解析为Hashtable 实例...

$properties = Invoke-Expression -Command $replacementText

这个正则表达式假定...

  • 所有属性值都需要引用。
  • 没有属性值包含;} 字符。

使用和上​​面一样的输入,下面的代码……

foreach ($pair in $testRecords.GetEnumerator())
{
    Write-Host '=================================================='

    $originalText = $pair.Value
    Write-Host "   Original text: $originalText"

    # Match the shortest text possible between "Name=" and a ";" or
    # a "}" and replace it with that same text surrounded by quotes
    $replacementText = $originalText -replace '(?<=[a-z]+=)(?<Value>.*?)(?=;|\s*})', '"${Value}"'
    if ([Object]::ReferenceEquals($originalText, $replacementText))
    {
        Write-Host 'Replacement text is indentical to original text'
    }
    else
    {
        Write-Host "Replacement text: $replacementText";
    }

    $properties = Invoke-Expression -Command $replacementText

    Write-Host "Replacement text evaluated to $($properties.GetType().Name) with Count = $($properties.Count)"
    $properties.GetEnumerator() `
        | Select-Object -Property `
            'Name', `
            'Value', `
            @{
                Name = 'PrintableValue';
                Expression = {
                    return $(
                        if ($_.Value -eq $null) {
                            '<null>'
                        } elseif ($_.Value.Length -eq 0) {
                            '<empty>'
                        } else {
                            $_.Value -replace '\s', [Char] 0x00B7 # Middle dot
                        }
                    )
                };
            } `
        | Out-Host
}

...产生这个输出...

==================================================
   Original text: @{}
Replacement text is indentical to original text
Replacement text evaluated to Hashtable with Count = 0
==================================================
   Original text: @{DekId=1}
Replacement text: @{DekId="1"}
Replacement text evaluated to Hashtable with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1     1


==================================================
   Original text: @{DekId=1+1=2}
Replacement text: @{DekId="1+1=2"}
Replacement text evaluated to Hashtable with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1+1=2 1+1=2


==================================================
   Original text: @{ }
Replacement text is indentical to original text
Replacement text evaluated to Hashtable with Count = 0
==================================================
   Original text: @{ DekId=1 }
Replacement text: @{ DekId="1" }
Replacement text evaluated to Hashtable with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1     1


==================================================
   Original text: @{ DekId=1+1=2 }
Replacement text: @{ DekId="1+1=2" }
Replacement text evaluated to Hashtable with Count = 1

Name  Value PrintableValue
----  ----- --------------
DekId 1+1=2 1+1=2


==================================================
   Original text: @{First=A B C;Second=X Y Z}
Replacement text: @{First="A B C";Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C;Second=X Y Z }
Replacement text: @{First="A B C";Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C;Second=X Y Z;}
Replacement text: @{First="A B C";Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C;Second=X Y Z; }
Replacement text: @{First="A B C";Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=X Y Z}
Replacement text: @{First="A B C"; Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=X Y Z }
Replacement text: @{First="A B C"; Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=X Y Z;}
Replacement text: @{First="A B C"; Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=X Y Z; }
Replacement text: @{First="A B C"; Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First  A B C A·B·C


==================================================
   Original text: @{First=;Second=X Y Z}
Replacement text: @{First="";Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=;Second=X Y Z }
Replacement text: @{First="";Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=;Second=X Y Z;}
Replacement text: @{First="";Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=;Second=X Y Z; }
Replacement text: @{First="";Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=; Second=X Y Z}
Replacement text: @{First=""; Second="X Y Z"}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=; Second=X Y Z }
Replacement text: @{First=""; Second="X Y Z" }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=; Second=X Y Z;}
Replacement text: @{First=""; Second="X Y Z";}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=; Second=X Y Z; }
Replacement text: @{First=""; Second="X Y Z"; }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second X Y Z X·Y·Z
First        <empty>


==================================================
   Original text: @{First=A B C;Second=}
Replacement text: @{First="A B C";Second=""}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C;Second= }
Replacement text: @{First="A B C";Second="" }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C;Second=;}
Replacement text: @{First="A B C";Second="";}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C;Second=; }
Replacement text: @{First="A B C";Second=""; }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=}
Replacement text: @{First="A B C"; Second=""}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second= }
Replacement text: @{First="A B C"; Second="" }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=;}
Replacement text: @{First="A B C"; Second="";}
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{First=A B C; Second=; }
Replacement text: @{First="A B C"; Second=""; }
Replacement text evaluated to Hashtable with Count = 2

Name   Value PrintableValue
----   ----- --------------
Second       <empty>
First  A B C A·B·C


==================================================
   Original text: @{DekId=; FieldId=1234; OriginalValue=; NewValue=1234}
Replacement text: @{DekId=""; FieldId="1234"; OriginalValue=""; NewValue="1234"}
Replacement text evaluated to Hashtable with Count = 4

Name          Value PrintableValue
----          ----- --------------
NewValue      1234  1234
OriginalValue       <empty>
DekId               <empty>
FieldId       1234  1234


==================================================
   Original text: @{DekId=; FieldId=7602; OriginalValue=; NewValue=Alice, Hamburgler}
Replacement text: @{DekId=""; FieldId="7602"; OriginalValue=""; NewValue="Alice, Hamburgler"}
Replacement text evaluated to Hashtable with Count = 4

Name          Value             PrintableValue
----          -----             --------------
NewValue      Alice, Hamburgler Alice,·Hamburgler
OriginalValue                   <empty>
DekId                           <empty>
FieldId       7602              7602


【讨论】:

    猜你喜欢
    • 2020-08-07
    • 2022-01-28
    • 1970-01-01
    • 2020-02-20
    • 1970-01-01
    • 1970-01-01
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多