Excel (VBA)

Excel VBAに関するフォーラムです。
  • 解決済みのトピックにはコメントできません。
このトピックは解決済みです。
質問

 
(Windows 10全般 : Excel 2019)
順番に並べたい
投稿日時: 23/03/13 16:39:54
投稿者: eco2019

フォルダーを読み込むマクロで、読み込んだらフォルダー名の順番が下記のようになります。1の次は2が来るようにできないでしょうか・・・
1.〇〇〇
10.〇〇〇
11.〇〇〇
12.〇〇〇
13.〇〇〇
14.〇〇〇
15.〇〇〇
16.〇〇〇
17.〇〇〇
18.〇〇〇
19.〇〇〇
2.〇〇〇
20.〇〇〇
21.〇〇〇
Dim ff, i
     
Range("E7:E" & ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row + 1).ClearContents
 
ff = Range("B3")
 
If ff <> "" Then
    i = 6
     
    'フォルダをリストする
    MyName = Dir(ff & "\", vbDirectory)
    Do While MyName <> ""
        If MyName <> "." And MyName <> ".." Then
            If (GetAttr(ff & "\" & MyName) And vbDirectory) = vbDirectory Then
                Range("E" & i) = MyName
                i = i + 1
            End If
        End If
        MyName = Dir
    Loop
     
    'ファイルをリストする
    MyName = Dir(ff & "\", vbNormal)
    Do While MyName <> ""
        If MyName <> "." And MyName <> ".." Then
            Range("E" & i) = MyName
            i = i + 1
        End If
        MyName = Dir
    Loop
End If

回答
投稿日時: 23/03/13 17:06:45
投稿者: 詠み人知らず

ff = Range("B3")
  
If ff <> "" Then
    i = 6
      
    'フォルダをリストする
    MyName = Dir(ff & "\", vbDirectory)
    Do While MyName <> ""
        If MyName <> "." And MyName <> ".." Then
            If (GetAttr(ff & "\" & MyName) And vbDirectory) = vbDirectory Then
                Range("E" & i) = MyName
                i = i + 1
            End If
        End If
        MyName = Dir
    Loop
     
    'ファイルをリストする
     
    Dim MyNames As Object
    Set MyNames = CreateObject("System.Collections.ArrayList")
 
    MyName = Dir(ff & "\", vbNormal)
    Do While MyName <> ""
        If MyName <> "." And MyName <> ".." Then
            MyNames.Add MyName
        End If
        MyName = Dir
    Loop
 
    MyNames.Sort

    For Each tmp In MyNames
        Range("E" & i) = tmp
        i = i + 1
    Next
End If

投稿日時: 23/03/13 17:20:37
投稿者: eco2019

こんにちは。すみません、有難うございます。
 
変わらないみたいです。

回答
投稿日時: 23/03/13 18:18:40
投稿者: 詠み人知らず

Sort効かないんですね。残念。
 
'================== モジュールの上のほうに追加してください。 ==================
#If VBA7 And Win64 Then
    '64Bitの場合
    Declare PtrSafe Function StrCmpLogicalW Lib "SHLWAPI.DLL" (ByVal lpStr1 As String, ByVal lpStr2 As String) As Long
#Else
    '32Bitの場合
    Declare Function StrCmpLogicalW Lib "SHLWAPI.DLL" (ByVal lpStr1 As String, ByVal lpStr2 As String) As Long
#End If
'================== モジュールの上のほうに追加してください。 ==================
 
ff = Range("B3")
   
If ff <> "" Then
    i = 6
       
    'フォルダをリストする
    MyName = Dir(ff & "\", vbDirectory)
    Do While MyName <> ""
        If MyName <> "." And MyName <> ".." Then
            If (GetAttr(ff & "\" & MyName) And vbDirectory) = vbDirectory Then
                Range("E" & i) = MyName
                i = i + 1
            End If
        End If
        MyName = Dir
    Loop
      
    'ファイルをリストする
      
    Dim MyNames As Object
    Set MyNames = CreateObject("System.Collections.ArrayList")
  
    MyName = Dir(ff & "\", vbNormal)
    Do While MyName <> ""
        If MyName <> "." And MyName <> ".." Then
            MyNames.Add MyName
        End If
        MyName = Dir
    Loop
     
    '追加
    Call SortByFilename(MyNames)
    '削除
    'MyNames.Sort
 
    For Each tmp In MyNames
        Range("E" & i) = tmp
        i = i + 1
    Next
End If
 
'============================ モジュールに追加してください。 ============================
'ソート用の関数
Sub SortByFilename(ByRef MyNames As Object)
    Dim i As Long
    Dim j As Long
    Dim tmp As String
 
    For i = 0 To MyNames.Count - 1
        For j = 0 To MyNames.Count - 1
            If StrCmpLogicalW(StrConv(MyNames(i), vbUnicode), StrConv(MyNames(j), vbUnicode)) < 0 Then
                tmp = MyNames(i)
                MyNames(i) = MyNames(j)
                MyNames(j) = tmp
            End If
        Next
    Next
End Sub
'============================ モジュールに追加してください。 ============================

回答
投稿日時: 23/03/13 18:28:34
投稿者: simple

回答いただいたように、StrCmpLogicalWを使う方法で対応可能だと思います。
 
もっと簡単に、1桁は頭に0を付けて2桁に変更するというのが簡単で実務的だと思います。
1.〇〇〇は
01.〇〇〇に手で修正してしまう。
# もともとソートが必要であれば、予めそうした数字の使い方をするのが一般的では?

回答
投稿日時: 23/03/15 06:58:21
投稿者: simple

StrCmpLogicalWを使った私なりのコードを挙げておきます。
 
(1)指定したフォルダの配下のサブフォルダ名を、エクスプローラと同様の並びで取り出します。
・まず、FileSystemObjectを使って、サブフォルダ名を取り出します。
   (Dirを使うよりも、 FileSystemObjectを使ったほうが簡潔に書けるので、
    こちらを採用してみました。FSOは結構使う人も多いと思います。)
 (なお、某さんは .NetのArrayListに書き込んでいますが、動的配列を扱うより簡潔に書けるから
   と想像します。気持ちは理解できます。
   ただ、質問者さんが慣れていないかもしれないので、あえて動的配列の例にしてみました。)
 
・次に、StrCmpLogicalWというAPI関数を使って、エクスプローラと同様の並びに並び変えます。
  (同一の結果になりますが、某さんのと若干違います。比較回数が少ない分、効率は良いと
    思います。)
     
(2)また、フォルダ直下のファイル名の列挙部分は省いています。今回の注目点ではないと
   思われます。
 (ただ、直下のファイルではなく、求めたサブフォルダの配下のファイルではないかとも
   想像しています。そうであれば言ってもらえば参考コードは手元にあり〼)
 
【参考コード例】

Option Explicit
#If VBA7 And Win64 Then
    '64Bitの場合
    Declare PtrSafe Function StrCmpLogicalW Lib "SHLWAPI.DLL" (ByVal lpStr1 As String, ByVal lpStr2 As String) As Long
#Else
    '32Bitの場合
    Declare Function StrCmpLogicalW Lib "SHLWAPI.DLL" (ByVal lpStr1 As String, ByVal lpStr2 As String) As Long
#End If

Sub test()
    Dim target      As String
    Dim fldNames()  As String
    Dim fso         As Object
    Dim fld         As Variant
    Dim ind         As Long
    Dim f           As Object
    Dim tmp         As Variant
    Dim i           As Long
    Dim j           As Long

    Set fso = CreateObject("Scripting.FileSystemObject")

    target = Range("B3")
    'target = "D:\MyDocuments\202303" ''デバッグ用
    For Each fld In fso.GetFolder(target).SubFolders
        ReDim Preserve fldNames(ind)
        fldNames(ind) = fld.Name
        ind = ind + 1
    Next

    'fldNamseをエクスプローラと同様の並び順に変換
    Call SortByFilename(fldNames)

    'フォルダ一覧の出力
    j = 6
    For Each fld In fldNames
        Cells(j, "E") = fld
        j = j + 1
    Next

    Set fso = Nothing
End Sub

'ソート用の関数
Sub SortByFilename(ByRef fldNames() As String)
    Dim i           As Long
    Dim j           As Long
    Dim tmp         As String
    Dim p           As Long

    For i = 0 To UBound(fldNames)
        For j = i + 1 To UBound(fldNames)
            If StrCmpLogicalW(StrConv(fldNames(i), vbUnicode), _
                              StrConv(fldNames(j), vbUnicode)) > 0 Then
                tmp = fldNames(i)
                fldNames(i) = fldNames(j)
                fldNames(j) = tmp
            End If
        Next
    Next
End Sub

【余談】
StrCmpLogicalWというAPI関数については、10年以上前ですが、
こちらの給湯室で議論したことがあります。(nさんに情報をいただいた)
なお、StrCmpLogicalW関数については、
https://www.excel-chunchun.com/entry/GetFileFolderList_04
が参考になります。(ネット検索して上位に来ただけです)
 
# この記事で使われている
# Arr = VBA.Array("X10Y1", "X10Y10", "X10Y2", "X1Y1", _
#         "X1Y10", "X1Y2", "X2Y1", "X2Y10", "X2Y2")
# という配列の例は、moug給湯室で議論したときに使用したものと同じでした。
# たしか、なにかのコードコンテスト(どのプログラム言語が一番簡潔に書けるか)に
# 使用された例だった記憶があります。
# なお、上記参照ページで引用している記事の作者(半角チルダさん)と n さんは同一人です。

投稿日時: 23/03/15 10:13:31
投稿者: eco2019

すみません、有難うございます。中々、上手くいかずにどうすればいいのか分からなくなっていました。
 
やってみます。有難うございます。

投稿日時: 23/03/15 10:35:36
投稿者: eco2019

StrCmpLogicalWという関数があるのですね。思ったようにいきました。すごいです。有難うございました。