为什么 `-lt` 对字符和字符串的行为不同?
Why is `-lt` behaving differently for chars and strings?
我最近 about using -lt
or -gt
with strings. My answer was based on something I've read earlier 说 -lt
一次比较每个字符串中的一个字符,直到 ASCII 值不等于另一个。届时结果 (lower/equal/greater) 决定。按照这个逻辑,"Less" -lt "less"
应该 return True
因为 L
的 ASCII 字节值比 l
低,但它不会:
[System.Text.Encoding]::ASCII.GetBytes("Less".ToCharArray())
76
101
115
115
[System.Text.Encoding]::ASCII.GetBytes("less".ToCharArray())
108
101
115
115
"Less" -lt "less"
False
看来我可能遗漏了一个关键的部分:测试不区分大小写
#L has a lower ASCII-value than l. PS doesn't care. They're equal
"Less" -le "less"
True
#The last s has a lower ASCII-value than t. PS cares.
"Less" -lt "lest"
True
#T has a lower ASCII-value than t. PS doesn't care
"LesT" -lt "lest"
False
#Again PS doesn't care. They're equal
"LesT" -le "lest"
True
然后我尝试测试 char 与单字符字符串:
[int][char]"L"
76
[int][char]"l"
108
#Using string it's case-insensitive. L = l
"L" -lt "l"
False
"L" -le "l"
True
"L" -gt "l"
False
#Using chars it's case-sensitive! L < l
([char]"L") -lt ([char]"l")
True
([char]"L") -gt ([char]"l")
False
为了比较,我尝试使用区分大小写的小于运算符,但它表示 L > l
,这与 -lt
returned 的字符相反。
"L" -clt "l"
False
"l" -clt "L"
True
比较是如何工作的,因为它显然不是使用 ASCII 值,为什么它对字符和字符串的表现不同?
不太确定这里 post 的内容,除了处理 strings/characters 时的比较都是正确的。如果您想要进行序数比较,请进行序数比较,然后您会得到基于此的结果。
Best Practices for Using Strings in the .NET Framework
[string]::Compare('L','l')
returns 1
和
[string]::Compare("L","l", [stringcomparison]::Ordinal)
returns -32
不确定要在此处添加什么以帮助澄清。
另见:Upper vs Lower Case
非常感谢 PetSerAl 提供的宝贵意见。
tl;博士:
-lt
和 -gt
通过 Unicode 代码点[=203] 比较 [char]
个实例数值 =].
- 令人困惑的是,
-ilt
、-clt
、-igt
、-cgt
也是如此——尽管它们只对 string[=203= 有意义] 操作数,但这是 PowerShell 语言本身的一个怪癖(见底部)。
-eq
(及其别名-ieq
),相比之下,比较[char]
个实例不区分大小写,这通常是 不一定 像不区分大小写的 字符串 比较(-ceq
再次比较 严格数字).
-eq
/-ieq
ultimately 也比较 numerically,但首先将操作数转换为大写等价物使用不变文化;因此,这种比较并不完全等同于 PowerShell 的 string 比较,后者还识别所谓的兼容序列(不同的字符或什至被认为具有相同含义的序列;参见 Unicode equivalence) 相等。
- 换句话说:PowerShell 特例 仅
-eq
/ -ieq
与 [char]
操作数 ,并且以 几乎相同的方式执行,但与不区分大小写的 string 比较.
这种区别会导致违反直觉的行为,例如 [char] 'A' -eq [char] 'a'
和 [char] 'A' -lt [char] 'a'
both 返回 $true
.
安全起见:
- 如果你想要数字(Unicode代码点)比较,总是转换为
[int]
。
如果你想要 string 比较,- 总是转换为
[string]
。
有关背景信息,请继续阅读。
PowerShell 通常有用的运算符重载有时会很棘手。
请注意,在 数字 上下文(无论是隐式还是显式)中,PowerShell 将 个字符([char]
([System.Char]
) 个实例) 数值, 通过它们的 Unicode 代码点(不是 ASCII)。
[char] 'A' -eq 65 # $true, in the 'Basic Latin' Unicode range, which coincides with ASCII
[char] 'Ā' -eq 256 # $true; 0x100, in the 'Latin-1 Supplement' Unicode range
[char]
的不同之处在于它的实例 相互比较 按原样 , 通过 Unicode 代码点,除了 -eq
/-ieq
.
ceq
、-lt
和 -gt
通过 Unicode 代码点直接 比较 ,并且 - 与直觉相反 - [=18] =]、-clt
、-igt
和 -cgt
:
[char] 'A' -lt [char] 'a' # $true; Unicode codepoint 65 ('A') is less than 97 ('a')
-eq
(及其别名-ieq
)首先将字符转换为大写,然后比较生成的 Unicode 代码点:
[char] 'A' -eq [char] 'a' # !! ALSO $true; equivalent of 65 -eq 65
这个佛教转折值得反思:这个和那个:在PowerShell的世界里,字符'A'都小于和等于'a',看你怎么比较了.
此外,直接或间接 - 在转换为大写字母后 - 比较 Unicode 代码点与将它们与 strings 进行比较不同,因为PowerShell 的 string 比较 另外 识别所谓的兼容序列,其中字符(甚至字符序列)被认为 "the same" 如果它们具有相同意思(参见Unicode equivalence);例如:
# Distinct Unicode characters U+2126 (Ohm Sign) and U+03A9 Greek Capital Letter Omega)
# ARE recognized as the "same thing" in a *string* comparison:
"Ω" -ceq "Ω" # $true, despite having distinct Unicode codepoints
# -eq/ieq: with [char], by only applying transformation to uppercase, the results
# are still different codepoints, which - compared numerically - are NOT equal:
[char] 'Ω' -eq [char] 'Ω' # $false: uppercased codepoints differ
# -ceq always applies direct codepoint comparison.
[char] 'Ω' -ceq [char] 'Ω' # $false: codepoints differ
请注意,使用前缀 i
或 c
来 明确地 指定大小写匹配行为不足以强制 字符串比较,尽管概念上运算符,例如-ceq
、-ieq
、-clt
、 -ilt
、-cgt
、-igt
只对字符串有意义。
实际上,i
和 c
前缀在应用于 -lt
和 [=16 时只是 被忽略 =]同时比较[char]
个操作数;事实证明(与我最初的想法不同),这是一个 一般的 PowerShell 陷阱 - 请参阅下面的解释。
顺便说一句:-lt
和 -gt
逻辑 string 比较是 not numeric,但基于 collation order(一种以 human 为中心的独立于代码点/字节值的排序方式),其中在 .NET 术语中由 cultures 控制(默认情况下由当前有效的文化控制,或者通过将 culture parameter 传递给方法)。
正如@PetSerAl 在评论中所展示的(与我最初声称的不同),PS 字符串比较使用 invariant culture,而不是当前文化,所以他们的行为是一样的,不管当前是什么文化。
幕后花絮:
正如@PetserAl 在评论中解释的那样,PowerShell 的 解析 不区分运算符的基本形式及其i
前缀形式;例如,-lt
和 -ilt
都被转换为 相同的 值,Ilt
.
因此,Powershell 不能 为 -lt
与 -ilt
、-gt
实施 不同的 行为] 与 igt
、... 相比,因为它在语法级别上对它们一视同仁。
这会导致某种违反直觉的行为,因为在比较区分大小写没有意义的数据类型时,运算符前缀实际上被忽略 - 而不是像人们预期的那样被迫使用字符串;例如:
"10" -cgt "2" # $false, because "2" comes after "1" in the collation order
10 -cgt 2 # !! $true; *numeric* comparison still happens; the `c` is ignored.
在后一种情况下,我希望使用 -cgt
将操作数强制转换为字符串,因为区分大小写的比较只是字符串比较中的一个有意义的概念,但这不是它的工作原理.
如果您想更深入地了解 PowerShell 的运作方式,请参阅下面@PetSerAl 的评论。
我最近 -lt
or -gt
with strings. My answer was based on something I've read earlier 说 -lt
一次比较每个字符串中的一个字符,直到 ASCII 值不等于另一个。届时结果 (lower/equal/greater) 决定。按照这个逻辑,"Less" -lt "less"
应该 return True
因为 L
的 ASCII 字节值比 l
低,但它不会:
[System.Text.Encoding]::ASCII.GetBytes("Less".ToCharArray())
76
101
115
115
[System.Text.Encoding]::ASCII.GetBytes("less".ToCharArray())
108
101
115
115
"Less" -lt "less"
False
看来我可能遗漏了一个关键的部分:测试不区分大小写
#L has a lower ASCII-value than l. PS doesn't care. They're equal
"Less" -le "less"
True
#The last s has a lower ASCII-value than t. PS cares.
"Less" -lt "lest"
True
#T has a lower ASCII-value than t. PS doesn't care
"LesT" -lt "lest"
False
#Again PS doesn't care. They're equal
"LesT" -le "lest"
True
然后我尝试测试 char 与单字符字符串:
[int][char]"L"
76
[int][char]"l"
108
#Using string it's case-insensitive. L = l
"L" -lt "l"
False
"L" -le "l"
True
"L" -gt "l"
False
#Using chars it's case-sensitive! L < l
([char]"L") -lt ([char]"l")
True
([char]"L") -gt ([char]"l")
False
为了比较,我尝试使用区分大小写的小于运算符,但它表示 L > l
,这与 -lt
returned 的字符相反。
"L" -clt "l"
False
"l" -clt "L"
True
比较是如何工作的,因为它显然不是使用 ASCII 值,为什么它对字符和字符串的表现不同?
不太确定这里 post 的内容,除了处理 strings/characters 时的比较都是正确的。如果您想要进行序数比较,请进行序数比较,然后您会得到基于此的结果。
Best Practices for Using Strings in the .NET Framework
[string]::Compare('L','l')
returns 1
和
[string]::Compare("L","l", [stringcomparison]::Ordinal)
returns -32
不确定要在此处添加什么以帮助澄清。
另见:Upper vs Lower Case
非常感谢 PetSerAl 提供的宝贵意见。
tl;博士:
-lt
和-gt
通过 Unicode 代码点[=203] 比较[char]
个实例数值 =].- 令人困惑的是,
-ilt
、-clt
、-igt
、-cgt
也是如此——尽管它们只对 string[=203= 有意义] 操作数,但这是 PowerShell 语言本身的一个怪癖(见底部)。
- 令人困惑的是,
-eq
(及其别名-ieq
),相比之下,比较[char]
个实例不区分大小写,这通常是 不一定 像不区分大小写的 字符串 比较(-ceq
再次比较 严格数字).-eq
/-ieq
ultimately 也比较 numerically,但首先将操作数转换为大写等价物使用不变文化;因此,这种比较并不完全等同于 PowerShell 的 string 比较,后者还识别所谓的兼容序列(不同的字符或什至被认为具有相同含义的序列;参见 Unicode equivalence) 相等。- 换句话说:PowerShell 特例 仅
-eq
/-ieq
与[char]
操作数 ,并且以 几乎相同的方式执行,但与不区分大小写的 string 比较.
这种区别会导致违反直觉的行为,例如
[char] 'A' -eq [char] 'a'
和[char] 'A' -lt [char] 'a'
both 返回$true
.安全起见:
- 如果你想要数字(Unicode代码点)比较,总是转换为
[int]
。
如果你想要 string 比较, - 总是转换为
[string]
。
- 如果你想要数字(Unicode代码点)比较,总是转换为
有关背景信息,请继续阅读。
PowerShell 通常有用的运算符重载有时会很棘手。
请注意,在 数字 上下文(无论是隐式还是显式)中,PowerShell 将 个字符([char]
([System.Char]
) 个实例) 数值, 通过它们的 Unicode 代码点(不是 ASCII)。
[char] 'A' -eq 65 # $true, in the 'Basic Latin' Unicode range, which coincides with ASCII
[char] 'Ā' -eq 256 # $true; 0x100, in the 'Latin-1 Supplement' Unicode range
[char]
的不同之处在于它的实例 相互比较 按原样 , 通过 Unicode 代码点,除了 -eq
/-ieq
.
ceq
、-lt
和-gt
通过 Unicode 代码点直接 比较 ,并且 - 与直觉相反 - [=18] =]、-clt
、-igt
和-cgt
:
[char] 'A' -lt [char] 'a' # $true; Unicode codepoint 65 ('A') is less than 97 ('a')
-eq
(及其别名-ieq
)首先将字符转换为大写,然后比较生成的 Unicode 代码点:
[char] 'A' -eq [char] 'a' # !! ALSO $true; equivalent of 65 -eq 65
这个佛教转折值得反思:这个和那个:在PowerShell的世界里,字符'A'都小于和等于'a',看你怎么比较了.
此外,直接或间接 - 在转换为大写字母后 - 比较 Unicode 代码点与将它们与 strings 进行比较不同,因为PowerShell 的 string 比较 另外 识别所谓的兼容序列,其中字符(甚至字符序列)被认为 "the same" 如果它们具有相同意思(参见Unicode equivalence);例如:
# Distinct Unicode characters U+2126 (Ohm Sign) and U+03A9 Greek Capital Letter Omega)
# ARE recognized as the "same thing" in a *string* comparison:
"Ω" -ceq "Ω" # $true, despite having distinct Unicode codepoints
# -eq/ieq: with [char], by only applying transformation to uppercase, the results
# are still different codepoints, which - compared numerically - are NOT equal:
[char] 'Ω' -eq [char] 'Ω' # $false: uppercased codepoints differ
# -ceq always applies direct codepoint comparison.
[char] 'Ω' -ceq [char] 'Ω' # $false: codepoints differ
请注意,使用前缀 i
或 c
来 明确地 指定大小写匹配行为不足以强制 字符串比较,尽管概念上运算符,例如-ceq
、-ieq
、-clt
、 -ilt
、-cgt
、-igt
只对字符串有意义。
实际上,i
和 c
前缀在应用于 -lt
和 [=16 时只是 被忽略 =]同时比较[char]
个操作数;事实证明(与我最初的想法不同),这是一个 一般的 PowerShell 陷阱 - 请参阅下面的解释。
顺便说一句:-lt
和 -gt
逻辑 string 比较是 not numeric,但基于 collation order(一种以 human 为中心的独立于代码点/字节值的排序方式),其中在 .NET 术语中由 cultures 控制(默认情况下由当前有效的文化控制,或者通过将 culture parameter 传递给方法)。
正如@PetSerAl 在评论中所展示的(与我最初声称的不同),PS 字符串比较使用 invariant culture,而不是当前文化,所以他们的行为是一样的,不管当前是什么文化。
幕后花絮:
正如@PetserAl 在评论中解释的那样,PowerShell 的 解析 不区分运算符的基本形式及其i
前缀形式;例如,-lt
和 -ilt
都被转换为 相同的 值,Ilt
.
因此,Powershell 不能 为 -lt
与 -ilt
、-gt
实施 不同的 行为] 与 igt
、... 相比,因为它在语法级别上对它们一视同仁。
这会导致某种违反直觉的行为,因为在比较区分大小写没有意义的数据类型时,运算符前缀实际上被忽略 - 而不是像人们预期的那样被迫使用字符串;例如:
"10" -cgt "2" # $false, because "2" comes after "1" in the collation order
10 -cgt 2 # !! $true; *numeric* comparison still happens; the `c` is ignored.
在后一种情况下,我希望使用 -cgt
将操作数强制转换为字符串,因为区分大小写的比较只是字符串比较中的一个有意义的概念,但这不是它的工作原理.
如果您想更深入地了解 PowerShell 的运作方式,请参阅下面@PetSerAl 的评论。