Powershell 为什么字符和字符串的“-lt”行为不同?

Powershell 为什么字符和字符串的“-lt”行为不同?,powershell,comparison,operators,string-comparison,Powershell,Comparison,Operators,String Comparison,最近,我开始考虑将-lt或-gt与字符串一起使用。我的答案是基于这样的说法,即-lt一次比较每个字符串中的一个字符,直到ASCII值不等于另一个为止。在这一点上,结果(较低/相等/较大)决定。根据该逻辑,“Less”-lt“Less”应返回True,因为L的ASCII字节值低于L,但它不: [System.Text.Encoding]::ASCII.GetBytes("Less".ToCharArray()) 76 101 115 115 [System.Text.Encoding]::ASC

最近,我开始考虑将
-lt
-gt
与字符串一起使用。我的答案是基于这样的说法,即
-lt
一次比较每个字符串中的一个字符,直到ASCII值不等于另一个为止。在这一点上,结果(较低/相等/较大)决定。根据该逻辑,
“Less”-lt“Less”
应返回
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

比较是如何工作的,因为它显然不是通过使用ASCII值来实现的,为什么字符和字符串的行为会有所不同?

不太确定在处理字符串/字符时,除了比较之外,在这里发布什么都是正确的。如果你想要一个有序的比较,做一个有序的比较,你会得到基于这个的结果

不确定在此添加什么来帮助澄清


另请参见:

不太确定在这里发布的内容,但在处理字符串/字符时,比较都是正确的。如果你想要一个有序的比较,做一个有序的比较,你会得到基于这个的结果

不确定在此添加什么来帮助澄清

另请参阅:

非常感谢他所有宝贵的意见

tl;博士

  • -lt
    -gt
    通过Unicode码点以数字方式比较
    [char]
    实例

    • 令人困惑的是,
      -ilt
      -clt
      -igt
      -cgt
      ——尽管它们只对字符串操作数有意义,但这是PowerShell语言本身的一个怪癖(见下图)
  • 相比之下,比较
    -eq
    (及其别名
    -ieq
    ),不区分大小写地比较
    [char]
    实例,这通常但不一定像不区分大小写的字符串比较(
    -ceq
    再次严格地进行数字比较)

    • -eq
      /
      -ieq
      最终也会进行数字比较,但首先会使用不变区域性将操作数转换为大写等效数;因此,此比较并不完全等同于PowerShell的字符串比较,后者还将所谓的兼容序列(不同字符,甚至被认为具有相同含义的序列;请参阅)视为相等
    • 换句话说:PowerShell特例仅使用
      [char]
      操作数执行
      -eq
      /
      -ieq
      的行为,其执行方式几乎是,但与不区分大小写的字符串比较不同
  • 这种区别会导致违反直觉的行为,例如
    [char]'A'-eq[char]'A'
    [char]'A'-lt[char]'A'
    都返回
    $true

  • 为了安全起见:

    • 如果要进行数字(Unicode码点)比较,请始终强制转换为
      [int]
    • 如果要进行字符串比较,请始终强制转换为
      [string]
有关背景信息,请继续阅读


PowerShell通常有用的运算符重载有时会很棘手

请注意,在数字上下文中(无论是隐式还是显式),PowerShell将字符(
[char]
[System.char]
)实例)
按其Unicode码点(非ASCII)进行数字处理

[char]
的与众不同之处在于,除了使用
-eq
/
-ieq
之外,它的实例在数字上与Unicode码点进行比较

  • ceq
    -lt
    -gt
    直接通过Unicode码点进行比较,并且-反直觉地-ilt,
    -clt
    -igt
    -cgt
  • -eq
    (及其别名
    -ieq
    )首先将字符转换为大写,然后比较生成的Unicode码点:
值得反思的是这个佛教转向:这个和那个:在PowerShell的世界中,角色“A”小于或等于“A”,这取决于你如何比较

另外,在转换为大写后,直接或间接比较Unicode代码点与将其作为字符串进行比较并不相同,因为PowerShell的字符串比较还识别所谓的兼容序列,其中字符(甚至字符序列)被视为“相同”如果它们具有相同的含义(参见);e、 g:

请注意,使用前缀
i
c
来明确指定大小写匹配行为不足以强制字符串比较
,即使概念上的运算符,如
-ceq
-ieq
-clt
-ilt
-cgt
-igt
仅对字符串有意义

有效地,在比较
[char]
操作数时,当应用于
-lt
-gt
时,
i
c
前缀被忽略;事实证明(与我最初的想法不同),这是一个一般的PowerShell陷阱——请参见下面的解释

另一方面:
-lt
-gt
字符串比较中的逻辑不是数字,而是基于排序顺序(一种以人为中心的排序方式,独立于代码点/字节值),在.NET术语中,排序由区域性控制(默认情况下为当前有效的方法,或将区域性参数传递给方法)。
正如@PetSerAl在评论中所展示的(与我最初所说的不同)
"L" -clt "l"
False

"l" -clt "L"
True
[string]::Compare('L','l')
returns 1
[string]::Compare("L","l", [stringcomparison]::Ordinal)
returns -32
[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] 'A' -lt [char] 'a'  # $true; Unicode codepoint 65 ('A') is less than 97 ('a')
[char] 'A' -eq [char] 'a' # !! ALSO $true; equivalent of 65 -eq 65
# 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
"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.