You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VB.NET自定义IComparer实现自然排序失效,求排查帮助

自然排序比较器失效的排查与修复

当前字符串排序结果如下:

Slide1.jpg
Slide10.jpg
Slide11.jpg
Slide12.jpg
Slide2.jpg
Slide3.jpg

需实现自然排序(按数字大小顺序排列),自行实现的IComparer(Of String)接口实现类NaturalComparer未生效,使用代码及原比较器实现如下:

使用代码

Dim files = Directory.GetFiles("c:\PowerPoint\Images", "*.jpg", SearchOption.AllDirectories).ToList()
files.Sort(New NaturalComparer(True))

原NaturalComparer实现

Public Class NaturalComparer
  Implements IComparer(Of String)
  Private _pos As Integer
  Private ReadOnly _order As Integer

  Public Sub New(Optional Ascending As Boolean = True)
    _order = If(Ascending, 1, -1)
  End Sub

  Private Shared Function RegexSplit(ByVal s As String) As String()
    Return Regex.Split(s, "(\d+) Then", RegexOptions.IgnoreCase)
  End Function

  Private Shared Function GetEmptyStrings() As Predicate(Of String)
    Return Function(s) String.IsNullOrEmpty(s)
  End Function

  Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
    Dim left As New List(Of String)(RegexSplit(x))
    Dim right As New List(Of String)(RegexSplit(y))

    left.RemoveAll(GetEmptyStrings())
    right.RemoveAll(GetEmptyStrings())

    _pos = 0
    For Each x In left
        If y.Count > _pos Then
            If Not Decimal.TryParse(x, Nothing) AndAlso Not Decimal.TryParse(right(_pos), Nothing) Then
                Dim result As Integer = String.Compare(x, right(_pos), True)
                If result <> 0 Then
                    Return result * _order
                Else
                    _pos += 1
                End If
            ElseIf Decimal.TryParse(x, Nothing) AndAlso Not Decimal.TryParse(right(_pos), Nothing) Then
                Return -1 * _order
            ElseIf Not Decimal.TryParse(x, Nothing) AndAlso Decimal.TryParse(right(_pos), Nothing) Then
                Return 1 * _order
            Else
                Dim result = Decimal.Compare(Decimal.Parse(x), Decimal.Parse(right(_pos)))
                If result = 0 Then
                    _pos += 1
                Else
                    Return result * _order
                End If
            End If
        Else
            Return -1 * _order
        End If
    Next

    Return _order
  End Function

End Class

问题排查

  1. 正则表达式错误:RegexSplit方法中的正则"(\d+) Then"完全不符合需求,本意是拆分字符串中的数字与非数字部分,但该正则会匹配包含数字加Then的片段,导致无法正确拆分目标字符串。正确的拆分正则应为"(\d+)",可将字符串拆分为非数字段与数字段交替的数组。
  2. 循环逻辑混乱:使用For Each x In left遍历左侧片段,同时用_pos索引右侧列表,当两侧片段数量不一致时,会出现索引不匹配或逻辑错误;循环结束后直接返回_order的逻辑错误,未考虑两侧片段长度不同的情况。
  3. 类型转换风险:直接使用Decimal.Parse转换字符串,若数字超出Decimal范围会抛出异常,应使用TryParse安全处理转换。

修复后的实现

Imports System.Text.RegularExpressions

Public Class NaturalComparer
    Implements IComparer(Of String)
    Private ReadOnly _order As Integer

    Public Sub New(Optional ascending As Boolean = True)
        _order = If(ascending, 1, -1)
    End Sub

    Private Shared Function RegexSplit(s As String) As String()
        ' 拆分数字与非数字部分,保留数字分组
        Return Regex.Split(s, "(\d+)")
    End Function

    Public Function Compare(x As String, y As String) As Integer Implements IComparer(Of String).Compare
        ' 处理空值情况
        If x Is Nothing And y Is Nothing Then Return 0
        If x Is Nothing Then Return -1 * _order
        If y Is Nothing Then Return 1 * _order

        ' 获取非空片段列表
        Dim leftParts = RegexSplit(x).Where(Function(part) Not String.IsNullOrEmpty(part)).ToList()
        Dim rightParts = RegexSplit(y).Where(Function(part) Not String.IsNullOrEmpty(part)).ToList()

        Dim minLength = Math.Min(leftParts.Count, rightParts.Count)

        ' 逐段比较
        For i = 0 To minLength - 1
            Dim leftPart = leftParts(i)
            Dim rightPart = rightParts(i)

            Dim leftIsNumber As Boolean, rightIsNumber As Boolean
            Dim leftNum As Decimal, rightNum As Decimal

            leftIsNumber = Decimal.TryParse(leftPart, leftNum)
            rightIsNumber = Decimal.TryParse(rightPart, rightNum)

            Select Case True
                Case leftIsNumber And rightIsNumber
                    ' 数字按数值比较
                    Dim numCompare = Decimal.Compare(leftNum, rightNum)
                    If numCompare <> 0 Then Return numCompare * _order
                Case Not leftIsNumber And Not rightIsNumber
                    ' 非数字按字符串忽略大小写比较
                    Dim strCompare = String.Compare(leftPart, rightPart, StringComparison.OrdinalIgnoreCase)
                    If strCompare <> 0 Then Return strCompare * _order
                Case Else
                    ' 数字优先排在前面(升序)
                    Return If(leftIsNumber, -1, 1) * _order
            End Select
        Next

        ' 前面片段均相同,长度更长的排在后面
        Return leftParts.Count.CompareTo(rightParts.Count) * _order
    End Function
End Class

修复后调用方式不变,即可实现预期的自然排序效果。

内容的提问来源于stack exchange,提问作者ExpertOnNothing

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 12:15:31