tech.guitarrapc.cóm

C#, PowerShell, Unity, Cloud, Serverless Technical Update and Features

PowerShellで文字列の比較をする際のTips

余り本件に関して書くことが無いのですが…一応簡単なサンプルを。 今回は、時々アレっ?って自分で思うため、テキストに含まれるかどうか/比較を見る方法のメモです。

サンプル

今回、これらを試してみます。
"abc".Contains("a")
"Abc".Contains("a")
""
"abc" -contains "a"
@("abc","bc","c") -contains "a"
@("abc","bc","c") -contains "c"
@("abc","bc","C") -contains "c"
""
"abc" -ccontains "a"
@("abc","bc","c") -ccontains "a"
@("abc","bc","c") -ccontains "c"
@("abc","bc","C") -ccontains "c"
""
"a" -in "abc"
"a" -in @("abc","bc","c")
"c" -in @("abc","bc","c")
"c" -in @("abc","bc","C")
""
"a" -cin "abc"
"a" -cin @("abc","bc","c")
"c" -cin @("abc","bc","c")
"c" -cin @("abc","bc","C")
""
"abc" -match "a"
"abc" -match "A"
"abc" -cmatch "a"
"abc" -cmatch "A"
""
[regex]::Match("abc","a")
""
"abc" -eq "a"
"a" -eq "a"
"A" -eq "a"
""
"abc" -ceq "a"
"a" -ceq "a"
"A" -ceq "a"
""
"abc".Equals("a")
"a".Equals("a")
"a".Equals("A")
""
'ー'.Contains('々')
'ー' -contains '々'
'ー' -ccontains '々'
'々' -in 'ー'
'々' -cin 'ー'
'ー' -match '々'
'ー' -cmatch '々'
'ー' -eq '々'
'ー' -ceq '々'
'ー'.Equals('々')
[Object]::ReferenceEquals('ー', '々')
結果を見てみましょう。
True #"abc".Contains("a")
False #"Abc".Contains("a")

False #"abc" -contains "a"
False #@("abc","bc","c") -contains "a"
True #@("abc","bc","c") -contains "c"
True #@("abc","bc","C") -contains "c"

False #"abc" -ccontains "a"
False #@("abc","bc","c") -ccontains "a"
True #@("abc","bc","c") -ccontains "c"
False #@("abc","bc","C") -ccontains "c"

False #"a" -in "abc"
False #"a" -in @("abc","bc","c")
True #"c" -in @("abc","bc","c")
True #"c" -in @("abc","bc","C")

False #"a" -cin "abc"
False #"a" -cin @("abc","bc","c")
True #"c" -cin @("abc","bc","c")
False #"c" -cin @("abc","bc","C")

True #"abc" -match "a"
True #"abc" -match "A"
True #"abc" -cmatch "a"
False #"Abc" -cmatch "A"

#[regex]::Match("abc","a")
Groups   : {a}
Success  : True
Captures : {a}
Index    : 0
Length   : 1
Value    : a


False #"abc" -eq "a"
True #"a" -eq "a"
True #"A" -eq "a"

False #"abc" -ceq "a"
True #"a" -ceq "a"
False #"A" -ceq "a"

False #"abc".Equals("a")
True #"a".Equals("a")
False #"a".Equals("A")

False #'ー'.Contains('々')
True #'ー' -contains '々'
True #'ー' -ccontains '々'
True #'々' -in 'ー'
True #'々' -cin 'ー'
False #'ー' -match '々'
False #'ー' -cmatch '々'
True #'ー' -eq '々'
True #'ー' -ceq '々'
False #'ー'.Equals('々')
False #[Object]::ReferenceEquals('ー', '々')
それぞれ見てみます。

contain()

このようなコードでした。
"abc".Contains("a")
"Abc".Contains("a")
'ー'.Contains('々')
結果はこうですね
True #"abc".Contains("a")
False #"Abc".Contains("a")
False #'ー'.Contains('々')
動作は、そのままです。 "Value".Contrains(x) の時、Valueにxが含まれるかをCase Sensitiveに判定します。

-contain

このようなコードでした。
"abc" -contains "a"
@("abc","bc","c") -contains "a"
@("abc","bc","c") -contains "c"
@("abc","bc","C") -contains "c"
'ー' -contains '々'
結果はこうですね
False #"abc" -contains "a"
False #@("abc","bc","c") -contains "a"
True #@("abc","bc","c") -contains "c"
True #@("abc","bc","C") -contains "c"
True #'ー' -contains '々'
.Contains()と異なるのが分かります。 @("Value1","Value2","Value3") -Contrains "x" の時、xが配列の要素"Value1"と"Value2"に含まれるかを判定します。 この判定は、配列要素との判定であり、配列の構成文字ではありません。 つまり、Value1="abc"とx="a"の場合場合は「"abc"に"a"が含まれるかどうかの判定ではありません」。 .Contain()が、"abc"と"a"の場合は「"abc"に含まれるかどうか判定」であるため、.Contain()と-containでは動作が違います。 時々アレ?っと思っちゃいます。

-ccontain

このようなコードでした。
"abc" -ccontains "a"
@("abc","bc","c") -ccontains "a"
@("abc","bc","c") -ccontains "c"
@("abc","bc","C") -ccontains "c"
'ー' -ccontains '々'
結果はこうですね
False #"abc" -ccontains "a"
False #@("abc","bc","c") -ccontains "a"
True #@("abc","bc","c") -ccontains "c"
False #@("abc","bc","C") -ccontains "c"
True #'ー' -ccontains '々'
-containは、Case Sensitiveに判定しませんでした。 Case Sencitiveに判定するには、-ccontainとします。

-in

このようなコードでした。
"a" -in "abc"
"a" -in @("abc","bc","c")
"c" -in @("abc","bc","c")
"c" -in @("abc","bc","C")
'々' -in 'ー'
結果はこうですね
False #"a" -in "abc"
False #"a" -in @("abc","bc","c")
True #"c" -in @("abc","bc","c")
True #"c" -in @("abc","bc","C")
True #'々' -in 'ー'
False #[Object]::ReferenceEquals('ー', '々')
-inは、PowerShell 3.0から追加されました。 動作は、-containのOperatorが逆になったものです。 "x" -in @("Value1","Value2","Value3") の時、xが配列の要素"Value1"と"Value2"に含まれるかを判定します。 -containに比べて、右辺と左辺が入れ替わっていることが分かります。

-cin

このようなコードでした。
"a" -cin "abc"
"a" -cin @("abc","bc","c")
"c" -cin @("abc","bc","c")
"c" -cin @("abc","bc","C")
'々' -cin 'ー'
結果はこうですね
False #"a" -cin "abc"
False #"a" -cin @("abc","bc","c")
True #"c" -cin @("abc","bc","c")
False #"c" -cin @("abc","bc","C")
True #'々' -cin 'ー'
-inは、Case Sensitiveに判定しませんでした。 Case Sencitiveに判定するには、-cinとします。

-match

このようなコードでした。
"abc" -match "a"
"abc" -match "A"
'ー' -match '々'
結果はこうですね
True #"abc" -match "a"
True #"Abc" -cmatch "a"
False #'ー' -match '々'
動作は、そのままです。 "Value" -match "x" の時、正規表現でValueにxが含まれるかを判定します。

-cmatch

このようなコードでした。
"abc" -cmatch "a"
"abc" -cmatch "A"
'ー' -cmatch '々'
結果はこうですね
True #"abc" -cmatch "a"
False #"abc" -cmatch "A"
False #'ー' -cmatch '々'
-matchは、Case Sensitiveに判定しませんでした。 Case Sencitiveに判定するには、-cmatchとします。

[regex]::Match()

このようなコードでした。
[regex]::Match("abc","a")
結果はこうですね
#[regex]::Match("abc","a")
Groups   : {a}
Success  : True
Captures : {a}
Index    : 0
Length   : 1
Value    : a
動作は、そのままです。 [regex]::Match("Value","x")の時、正規表現でValueにxが含まれるかをCase Sensitive判定します。

-eq

このようなコードでした。
"abc" -eq "a"
"a" -eq "a"
"A" -eq "a"
'ー' -eq '々'
結果はこうですね
False #"abc" -eq "a"
True #"a" -eq "a"
True #"A" -eq "a"
True #'ー' -eq '々'
動作は、そのままと思いきや、'ー' と '々' がTrue? ここはトラップになりそうな…。 さて、少し見てみましょう。 ここによると、基本的には、参照比較ではなく値比較のようですが、SecureStringのように参照比較しかクラスにない場合は、参照比較を行うようです。
PowerShell’s -EQ Operator: Reference Equality vs Value Equality
そしてここで詳しく解説されています。
PowerShell equality operator not a symmetric relation?
この理解が必要なようです。
-eq in PowerShell is not an equivalence relation.
動的型付け、というのがポイントといえるようです。(PowerShellは、左オペラントの型に右オペラントをキャストしようとします) ……しかし、 'ー' と '々' がTrueなのは……んー。 booleanにも癖があるようです。 これはどうなるでしょうか。
[ConsoleColor]::Black -eq $true
$true -eq [ConsoleColor]::Black
結果です。
False
True
順序を入れかえると結果が変わりました。 $nullに対しても、同様の癖があるようです。 配列時の-eqのフィルタ挙動と、$nullが含まれる際の動作については、牟田口先生の解説が参考になります。
if($array -eq $null) には要注意!
$nullに関しては、「分からない」を示すものと考えればよさそうですね。 これについては、SQLの記事ですがbleis先生が参考になります。
ぐるぐる~ - SQLアンチパターン
一応、現在の理解は以下です。
Value -eq x の時に、Value と x を比較する。 基本的には値比較だが、参照比較になりうる。 特に、PowerShellは左オペランドの型に左右されるため注意。 また、boolean/null判定する場合は、$true -eq Value あるいは $null -eq Valueのように順序に気を付ける。 配列で利用した場合にフィルタ動作を行う。

-ceq

このようなコードでした。
"abc" -ceq "a"
"a" -ceq "a"
"A" -ceq "a"
'ー' -ceq '々'
結果はこうですね
False #"abc" -ceq "a"
True #"a" -ceq "a"
False #"A" -ceq "a"
'ー' -ceq '々'
-eqは、Case Sensitiveに判定しませんでした。 Case Sencitiveに判定するには、-ceqとします。

.Equals()

このようなコードでした。
"abc".Equals("a")
"a".Equals("a")
"a".Equals("A")
'ー'.Equals('々')
結果はこうですね
False #"abc".Equals("a")
True #"a".Equals("a")
False #"a".Equals("A")
False #'ー'.Equals('々')
動作は、そのままですね。あー、良かった。 "Value".Equals(x) の時、Valueとxが一致するかをCase Sensitiveに判定します。

[Object]::ReferenceEquals()

このようなコードでした。
[Object]::ReferenceEquals('ー', '々')
結果はこうですね
False #[Object]::ReferenceEquals('ー', '々')
動作は、そのままですね。 [Object]::ReferenceEquals(Value, x)の時、Valueとxの参照先が一致するかを判定します。

まとめ

単純に文字列を比較するなら…この辺が適してそうですね。
.Contains()
.Equals()
-match
[regex]::Match()
オブジェクトの比較時には -eq を頻繁に利用しますが、少し注意をした方がいいようです。

参考

Windows PowerShell Version 3 Simplified Syntax Does -eq keyword in power shell test reference equality or use Object.Equals() Part-5: Text Manipulation in PowerShell using .Contains() and .Equals() methods