全て!
標準の INI ファイル構造をハッシュテーブルと配列に基づくものに変換するコードを書いています。たとえば、以下に示す INI スニペットは次のとおりです。
[Book]
title="Moby Dick"
author="Herman Melville"
genre=fiction
genre=fantasy
[Book]
title="James and the Giant Peach"
author="Roald Dahl"
genre="fiction"
次のようになります。
@{
"Book" = [
@{
"title" = ["Moby Dick"];
"author" = ["Herman Melville"];
"genre" = ["fiction","fantasy"]
},
@{
"title" = ["James and the Giant Peach"];
"author" = ["Roald Dahl"];
"genre" = ["fiction"]
}
]
}
複数の同じ名前のセクションと複数の同じ名前のプロパティを持つセクションを持つ INI ファイルを説明したかったため、このデータ構造になっています。ツリーを使用して同じことを行うことができることは知っていますが、その速度についてはあまり気にしません。これを書いた時点では実装が簡単でした。
これは、セクション内のすべてのプロパティをそれらの値に関連付けるハッシュテーブルを更新することによって行われ (これを と呼びましょう$items
)、次に、各セクションを前述のハッシュテーブルと関連付けるメイン ハッシュテーブルを更新します (これを と呼びましょう$results
)。ハッシュテーブルはグローバルであり、プロパティ ハッシュテーブルは新しいセクションへの反復時にクリアされます。
これを実行してうまく動作するコードを書いていたのですが、Powershell がハッシュテーブルをクリアする方法について数分間困惑しました。$items
を使用してクリアすると$items.Clear()
、のすべての要素に関連付けられたハッシュテーブル$results
もクリアされます。ただし、 と言って「クリア」すると、これは起こりません$items=@{}
。
前者の場合のハッシュテーブルはすべてグローバルハッシュテーブルへの参照であり、後者の場合は独立したオブジェクトであるため、これが発生すると想定しています。これは何が起こっているのですか?
コードは以下です。これが紛らわしい場合は申し訳ありません (そして、それは典型的な Powershell 構文ではありません)。複製するに$items = @{}
は、次のように置き換えます。$items = $items.Clear()
function parse ($file) {
$result = @{}
if (!(Test-Path $file)) {
write-host "File does not exist: $file"
} elseif (!($data = cat $file)) {
write-host "File has no data: $file"
} else {
$last_value = ""
$last_data = ""
$items = @{}
foreach ($line_raw in $data | ?{$_.Trim().Length -gt 0}) {
$line = $line_raw.TrimStart().TrimEnd()
$first_char = $line[0]
switch ($first_char) {
';' {
continue
}
'[' {
$key = $line -replace "\[|\]",""
if ($last_key) {
if (!($result.ContainsKey($last_key)))
{ $result.Add($last_key,@($items)) }
else
{ $result[$last_key] += $items }
if ($last_key -ne $key)
{ $items = @{} }
}
$last_key = $key
}
default {
$item , $data = $(
$sep = $line.indexOf('=')
$line.substring(0,$sep) , $line.substring($sep+1)
)
if (!$items.ContainsKey($item))
{ $items.Add($item,@($data)) }
else
{ $items[$item] += $data }
}
}
}
$result
}
}