代码之家  ›  专栏  ›  技术社区  ›  ic3

字符+和的奇怪排序/

  •  -1
  • ic3  · 技术社区  · 6 年前

    我们有一位客户要求我们以这种方式对一些字符串进行排序(为了与旧工具兼容):

      A IR / 
      A IR +100bp 
      B IR / 
      B IR +100bp 
    

    正如您可以看到的,“/”和“+”是颠倒的,标准顺序是相反的

      A IR +100bp 
      A IR / 
      B IR +100bp 
      B IR / 
    

    这两个字符都是低ASCII表的一部分,非常稳定。

    有谁知道第一个顺序来自哪种语言或算法(我想这不是最后一个惊喜)?

    1 回复  |  直到 6 年前
        1
  •  2
  •   JosefZ    6 年前

    简单的回答是:因为它使用 [String]::Compare() method 这将返回相同的结果 Thanks briantist

    [String]::Compare('/','+')
    # -1
    # Less than zero: `strA` precedes `strB` in the sort order
    

    较长的答案是:这不是一个问题 ASCII / Unicode codepoint 但是 Unicode字符串比较 定义在 Unicode Collation Algorithm report .

    后一份文件指出了 Default Unicode Collation Element Table 按照 Allkeys

    002B  ; [*063F.0020.0002] # PLUS SIGN
    002F  ; [*03A1.0020.0002] # SOLIDUS
    

    下面的(有趣的)示例是用( .NET -基于 )就像我不说话一样 Java :

    ( '+-/*+−÷×' | 
        Get-CharInfo | 
            Format-Table -HideTableHeaders | 
                Out-String
    ).Split( [System.Environment]::NewLine,
             [System.StringSplitOptions]::RemoveEmptyEntries ) |
        ForEach-Object { $_.Trim() } |
            Sort-Object -Unique
    

    结果:

    - U+002D     DashPunctuation Hyphen-Minus
    − U+2212          MathSymbol Minus Sign
    * U+002A    OtherPunctuation Asterisk
    / U+002F    OtherPunctuation Solidus
    + U+002B          MathSymbol Plus Sign
    × U+00D7          MathSymbol Multiplication Sign
    ÷ U+00F7          MathSymbol Division Sign
    

    为了完整性起见 Get-CharInfo 功能定义如下:

    <#
    _get-CharInfo_1.1.ps1
    
    Origin   by: http://poshcode.org/5234
                 sorry, the above link is not available; last time checked 2018-05-07
    Improved by: https://stackoverflow.com/users/3439404/josefz
    
    Use this like this: "ábč",([char]'x'),0xBF | Get-CharInfo
    
    Activate dot-sourced like this (apply a real path instead of .\):
    
    . .\_get-CharInfo_1.1.ps1
    #>
    
    Add-Type -Name UName -Namespace Microsofts.CharMap -MemberDefinition $(
        switch ("$([System.Environment]::SystemDirectory -replace 
                    '\\', '\\')\\getuname.dll") {
        {Test-Path -LiteralPath $_ -PathType Leaf} {@"
    [DllImport("${_}", ExactSpelling=true, SetLastError=true)]
    private static extern int GetUName(ushort wCharCode, 
        [MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder buf);
    
    public static string Get(char ch) {
        var sb = new System.Text.StringBuilder(300);
        UName.GetUName(ch, sb);
        return sb.ToString();
    }
    "@
        }
        default {'public static string Get(char ch) { return "???"; }'}
        })
    
    function Get-CharInfo {
        [CmdletBinding()]
        [OutputType([System.Management.Automation.PSCustomObject],[System.Array])]
        param(
            [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true)]
            $InputObject
        )
        begin {
            Set-StrictMode -Version latest
            function out {
                param(
                    [Parameter(Position=0, Mandatory=$true )] $ch,
                    [Parameter(Position=1, Mandatory=$false)]$nil=''
                     )
                if (0 -le $ch -and 0xFFFF -ge $ch) {
                    [pscustomobject]@{
                        Char = [char]$ch
                        CodePoint = 'U+{0:X4}' -f $ch
                        Category = [System.Globalization.CharUnicodeInfo]::GetUnicodeCategory($ch)
                        Description = [Microsofts.CharMap.UName]::Get($ch)
                    }
                } elseif (0 -le $ch -and 0x10FFFF -ge $ch) {
                    $s = [char]::ConvertFromUtf32($ch)
                    [pscustomobject]@{
                        Char = $s
                        CodePoint = 'U+{0:X}' -f $ch
                        Category = [System.Globalization.CharUnicodeInfo]::GetUnicodeCategory($s, 0)
                        Description = '???' + $nil
                    }
                } else {
                    Write-Warning ('Character U+{0:X} is out of range' -f $ch)
                }
            }
        }
        process {
            if ($PSBoundParameters['Verbose']) {
                Write-Warning "InputObject type = $($InputObject.GetType().Name)"}
            if ($null -cne ($InputObject -as [char])) {
                #Write-Verbose "A $([char]$InputObject) InputObject character"
                out $([int][char]$InputObject) ''
            } elseif ($InputObject -isnot [string] -and $null -cne ($InputObject -as [int])) {
                #Write-Verbose "B $InputObject InputObject"
                out $([int]$InputObject) ''
            } else {
                $InputObject = [string]$InputObject
                #Write-Verbose "C $InputObject InputObject.Length $($InputObject.Length)"
                for ($i = 0; $i -lt $InputObject.Length; ++$i) {
                    if (  [char]::IsHighSurrogate($InputObject[$i]) -and 
                          (1+$i) -lt $InputObject.Length -and 
                          [char]::IsLowSurrogate($InputObject[$i+1])) {
                        $aux = ' 0x{0:x4},0x{1:x4}' -f [int]$InputObject[$i], 
                                                       [int]$InputObject[$i+1]
                        Write-Verbose "surrogate pair $aux at position $i" 
                        out $([char]::ConvertToUtf32($InputObject[$i], $InputObject[1+$i])) $aux
                        $i++
                    } else {
                        out $([int][char]$InputObject[$i]) ''
                    }
                }
            }
        }
    }