Listviewでのマイナスを含む数字のソート


shellken  2007-06-28 16:16:47  No: 143707

VB6掲示板に間違って投稿してしまったため再投稿です。
Listviewのコラムをクリックして順番をソートするプログラムですがうまくいかない部分があります。
以下でほぼソートできるのですが...

Form1にListview1を貼り付けます。
以下を実行すると、数字のプラスマイナスを考えて、並べ替えが出来ませんが数字のプラスマイナスをも考慮したソートが出来るようにするにはどうしたらいいでしょうか?

'******************************************************************
Public Class LVSort

    'IComparerインターフェイス
    Implements IComparer

    Private mOrder As SortOrder = SortOrder.Ascending  'ソート順(昇順・降順)
    Private mKey As Integer = 0                        'ソート列

    'ソート順(昇順・降順)プロパティ
    Public Property Order() As SortOrder
        Get
            Return mOrder
        End Get
        Set(ByVal Value As SortOrder)
            mOrder = Value
        End Set
    End Property

    'ソート列プロパティ
    Public Property Key() As Integer
        Get
            Return mKey
        End Get
        Set(ByVal Value As Integer)
            mKey = Value
        End Set
    End Property

    '比較結果を返す
    Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements IComparer.Compare
        Dim intRet As Integer

        Try
            '比較用リストアイテム格納変数
            Dim sx As ListViewItem = CType(x, ListViewItem)
            Dim sy As ListViewItem = CType(y, ListViewItem)

            '文字列を比較し、値を格納
            intRet = CompareString(sx.SubItems(mKey).Text, sy.SubItems(mKey).Text)

            '降順のときは結果を逆転
            If mOrder = SortOrder.Descending Then
                intRet = -intRet
            End If

        Catch ex As Exception

            Debug.WriteLine(ex.Message.ToString)

        End Try

        '結果を返す
        Return intRet

    End Function

    '文字列内の数値として認識できる部位を検査し、比較した値を返す
    Private Function CompareString(ByVal str1 As String, ByVal str2 As String) As Integer
        Dim chrNum() As Char = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
        Dim i As Integer
        Dim intPos As Integer
        Dim intLength As Integer
        Dim intNext1 As Integer
        Dim intNext2 As Integer
        Dim intRet As Integer
        Dim idx As Integer
        Dim idx1 As Integer
        Dim idx2 As Integer
        Dim lngNum1 As Long = -1
        Dim lngNum2 As Long = -1

        'はじめの文字列から数値として認識できる部分を探す
        idx1 = str1.IndexOfAny(chrNum)

        '見つかった場合
        If idx1 <> -1 Then
            '位置を格納
            intPos = idx1

            '長さの初期値
            intLength = 1

            '見つかった位置から文字列の終わりまで検査
            For i = idx1 + 1 To str1.Length - 1
                '数値として認識できるか検査
                idx = str1.Substring(i, 1).IndexOfAny(chrNum)

                '数値として認識できない場合は抜ける
                If idx = -1 Then Exit For

                '長さを格納
                intLength += 1
            Next

            '数値をLong値として格納
            lngNum1 = CType(str1.Substring(intPos, intLength), Long)

            '文字列の途中で終了した場合
            If intPos + intLength <> str1.Length Then
                'ループを抜けたポイントを格納
                intNext1 = intPos + intLength
            End If

        End If

        '次の文字列から数値として認識できる部分を探す
        idx2 = str2.IndexOfAny(chrNum)

        '見つかった場合
        If idx2 <> -1 Then
            '位置を格納
            intPos = idx2

            '長さの初期値
            intLength = 1

            '見つかった位置から文字列の終わりまで検査
            For i = idx2 + 1 To str2.Length - 1
                '数値として認識できるか検査
                idx = str2.Substring(i, 1).IndexOfAny(chrNum)

                '数値として認識できない場合は抜ける
                If idx = -1 Then Exit For

                '長さを格納
                intLength += 1
            Next

            '数値をLong値として格納
            lngNum2 = CType(str2.Substring(intPos, intLength), Long)

            '文字列の途中で終了した場合
            If intPos + intLength <> str2.Length Then
                'ループを抜けたポイントを格納
                intNext2 = intPos + intLength
            End If

        End If

        '数値が認識されなかったか、
        'または同じ位置ではない場合
        If idx1 = -1 Or idx2 = -1 Or (idx1 <> idx2) Then
            '単純比較を行なう
            intRet = String.Compare(str1, str2)
        Else

            '文字列の先頭が数値として認識されたか、
            'または数値より前の文字列が同じ場合
            If (idx1 = 0 And idx2 = 0) Or _
                (str1.Substring(0, str1.Length - (str1.Length - idx1)) = _
                str2.Substring(0, str2.Length - (str2.Length - idx2))) Then

                '数値として認識でき、ひとつめが大きい場合
                If lngNum1 <> -1 And lngNum2 <> -1 And lngNum1 > lngNum2 Then
                    '入れ替える値を格納
                    intRet = 1
                ElseIf lngNum1 <> -1 And lngNum2 <> -1 And lngNum1 < lngNum2 Then
                    '数値として認識でき、ひとつめが小さい場合は
                    '入れ替えない値を格納
                    intRet = -1
                ElseIf intNext1 <> 0 And intNext2 <> 0 Then
                    '文字列の最後まで調べていない場合は再帰
                    intRet = CompareString(str1.Substring(intNext1), str2.Substring(intNext2))
                Else
                    '入れ替えない値を格納
                    intRet = -1
                End If
            Else
                '数値より前の文字列が違う場合は単純比較
                intRet = String.Compare(str1, str2)
            End If
        End If

        '値を返す
        Return intRet

    End Function

End Class

'*********************************************************************
Public Class Form1
    Inherits System.Windows.Forms.Form

#Region " Windows フォーム デザイナで生成されたコード "

    Public Sub New()
        MyBase.New()
        InitializeComponent()
    End Sub

    Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
        If disposing Then
            If Not (components Is Nothing) Then
                components.Dispose()
            End If
        End If
        MyBase.Dispose(disposing)
    End Sub

    Private components As System.ComponentModel.IContainer

    Friend WithEvents ListView1 As System.Windows.Forms.ListView
    <System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
        Me.ListView1 = New System.Windows.Forms.ListView
        Me.SuspendLayout()
        '
        'ListView1
        '
        Me.ListView1.Font = New System.Drawing.Font("MS ゴシック", 9.75!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, CType(128, Byte))
        Me.ListView1.HideSelection = False
        Me.ListView1.Location = New System.Drawing.Point(12, 12)
        Me.ListView1.Name = "ListView1"
        Me.ListView1.Size = New System.Drawing.Size(404, 196)
        Me.ListView1.TabIndex = 1
        Me.ListView1.UseCompatibleStateImageBehavior = False
        '
        'Form1
        '
        Me.AutoScaleBaseSize = New System.Drawing.Size(5, 12)
        Me.ClientSize = New System.Drawing.Size(426, 223)
        Me.Controls.Add(Me.ListView1)
        Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
        Me.MaximizeBox = False
        Me.Name = "Form1"
        Me.Text = "リストビューのソート"
        Me.ResumeLayout(False)

    End Sub

#End Region

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        'リストビューの初期設定
        ListView1.View = View.Details                        '詳細表示
        ListView1.HeaderStyle = ColumnHeaderStyle.Clickable  'カラムヘッダのクリック可能

        'リストビューの詳細表示用のヘッダ作成
        ListView1.Columns.Add("アイテム", 100, HorizontalAlignment.Left)
        ListView1.Columns.Add("サブ1", 100, HorizontalAlignment.Left)
        ListView1.Columns.Add("サブ2", 100, HorizontalAlignment.Right)

        ListView1.Items.Add("あああああ")
        ListView1.Items(0).SubItems.Add("fffff")
        ListView1.Items(0).SubItems.Add("33333")
        ListView1.Items.Add("いいいいい")
        ListView1.Items(1).SubItems.Add("ddddd")
        ListView1.Items(1).SubItems.Add("44444")
        ListView1.Items.Add("ううううう")
        ListView1.Items(2).SubItems.Add("aaaaa")
        ListView1.Items(2).SubItems.Add("-66666")
        ListView1.Items.Add("えええええ")
        ListView1.Items(3).SubItems.Add("eeeee")
        ListView1.Items(3).SubItems.Add("88888")
        ListView1.Items.Add("おおおおお")
        ListView1.Items(4).SubItems.Add("bbbbb")
        ListView1.Items(4).SubItems.Add("-77777")
        ListView1.Items.Add("かかかかか")
        ListView1.Items(5).SubItems.Add("hhhhh")
        ListView1.Items(5).SubItems.Add("55555")
        ListView1.Items.Add("ききききき")
        ListView1.Items(6).SubItems.Add("ggggg")
        ListView1.Items(6).SubItems.Add("-11111")
        ListView1.Items.Add("くくくくく")
        ListView1.Items(7).SubItems.Add("ccccc")
        ListView1.Items(7).SubItems.Add("22222")

    End Sub

    'カラムヘッダのクリック
    Private Sub ListView1_ColumnClick(ByVal sender As Object, ByVal e As System.Windows.Forms.ColumnClickEventArgs) Handles ListView1.ColumnClick
        Static blnOrderFlg As Boolean
        Dim ccLVSort As LVSort = New LVSort

        With ccLVSort
            If blnOrderFlg Then
                '昇順
                .Order = SortOrder.Ascending
            Else
                '降順
                .Order = SortOrder.Descending
            End If

            'クリックされた列によりソートキーを指定
            .Key = e.Column
        End With

        'ListViewItemSorterプロパティへ、ソートの比較演算子を表すカスタムクラスを指定
        ListView1.ListViewItemSorter = ccLVSort

        'フラグ変数を反転
        blnOrderFlg = Not blnOrderFlg

    End Sub

   
End Class
'*********************************************************************


通り  2007-06-28 18:07:21  No: 143708

移動元
http://madia.world.coocan.jp/cgi-bin/VBBBS/wwwlng.cgi?print+200706/07060056.txt

>VB6掲示板に間違って投稿してしまったため再投稿です。
それは仕方ありませんが、あちらで回答を頂いているのに無視してよい
理由にはなりません。


大吉末吉  2007-06-28 19:51:10  No: 143709

#  > あちらで回答
#  は、ちょっと勘違いしていたようです。

>         Dim chrNum() As Char = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
>         idx1 = str1.IndexOfAny(chrNum)

一文字目の判定時に、
"-"と"+"も対象文字に含めてはどうでしょう?

#つまり、「一文字目用」と「二文字目以降用」の2種類用意する。

ただ・・・"-A123"との時とが問題ですね・・・

「"+"や"-"だけの時は、続きから再度検索する」とかの処理も必要かも。


shellken  2007-06-28 20:25:29  No: 143710

勝手に移動して申し訳ありませんでした。
これからは気をつけます。
通りさん、異動元異動先をリンクしていただきありがとうございます。

大吉末吉さん、回答ありがとうございます。
どうも、難しいのですががんばってみます。
すっきりうまくいく方法はないものでしょうか?


我龍院  2007-06-29 02:41:07  No: 143711

>すっきりうまくいく方法はないものでしょうか?
ソートの条件(仕様)がわからないのでなんとも・・・


大吉末吉  2007-06-29 05:13:11  No: 143712

コードから、仕様を
『「最初に見つかった数値と看做せる一連の文字列」を数値として比較』とすると・・・

正規表現を使って切り出してしまうとか・・・
例えば、
-----------------------------------------------------
        Dim pat As String = "[+-]?[0-9]+"
        Dim pat As String = "[+-]?[0-9]+"
        Dim r As System.Text.RegularExpressions.Regex = _
            New System.Text.RegularExpressions.Regex(pat, _
            System.Text.RegularExpressions.RegexOptions.IgnoreCase)
        Dim txt As String
        Dim m As System.Text.RegularExpressions.Match
        '-----
        txt = "ABC-DEF-1000-2000"
        m = r.Match(txt)
        If m.Success = True Then
            MsgBox(m.Groups.Item(0).ToString)
        Else
            MsgBox("NotFound")
        End If
-------------------------------------------------
こうすると、「-1000」が一発で見つかります。


shellken  2007-06-30 07:28:54  No: 143713

我龍院さん

ソートの条件はたとえば
-1.12
-2.5
3.2
1.23
などの適当な4個の小数があって大きい順にきっちり並べばいいのですが、
上のコードでは大きい順にソートしたとき
3.2
-2.5
1.23
-1.12
のようにマイナスが無視された数字としてソートされます。
そこを何とかしたいのです。


YuO  2007-07-12 00:23:37  No: 143714

ListViewItemを継承したクラスを作り,そこに元データを保持するようにしておきます。
そうすれば,ソート時に元データを使ってソートができるため,わざわざ変換する必要がありません。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加