Làm sao để không phải khai báo biến toàn cục trong code này

Liên hệ QC

Quang_Hải

Thành viên gạo cội
Tham gia
21/2/09
Bài viết
6,041
Được thích
7,932
Nghề nghiệp
Làm đủ thứ
Mình có viết đoạn code để lấy tất cả file trong thư mục mẹ và thư mục con.
Nhưng loay hoay mãi không biết làm sao để không phải khai báo biến toàn cục.
Các anh chị xem qua code và vui lòng xin cho ý kiến.
PHP:
Public k As Long, Res()
Sub Main()
Dim strPath As String, fso As Object, chk As Boolean
Set fso = CreateObject("Scripting.FileSystemObject")
   With Application.FileDialog(msoFileDialogFolderPicker)
      chk = .Show
      If Not chk Then Exit Sub
      strPath = .SelectedItems(1)
   End With
   If Len(strPath) Then Call GetAllFiles(strPath, fso)
   If k Then [A5].Resize(k) = Application.Transpose(Res)
   k = 0
End Sub
Function GetAllFiles(ByVal StrFolder As String, ByRef fso As Object)
Dim objFolder As Object, objSubFolder As Object, File
Set objFolder = fso.GetFolder(StrFolder)
For Each objSubFolder In objFolder.SubFolders
   Call GetAllFiles(objSubFolder.path, fso)
   For Each File In objSubFolder.Files
      k = k + 1
      ReDim Preserve Res(1 To k)
      Res(k) = fso.GetBaseName(File)
   Next
Next objSubFolder
End Function
 
Em cũng viết thủ tục lấy list toàn bộ file trong thư mục như thế này (đã post diễn đàn rồi) nhưng cũng phải khai báo 2 biến toàn cục như anh, hóng các bài trả lời tại topic của anh.|||||
 
Upvote 0
Em cũng viết thủ tục lấy list toàn bộ file trong thư mục như thế này (đã post diễn đàn rồi) nhưng cũng phải khai báo 2 biến toàn cục như anh, hóng các bài trả lời tại topic của anh.|||||
Nếu dùng cách khác thì mình có thể, nhưng muốn thử xem dùng fso có xử được hay không.
 
Upvote 0
Chưa kiểm tra code, nhưng Thử dùng cái này xem sao:

Mã:
Sub Main()
    Static k As Long, Res()
    ''...
End Sub
 
Upvote 0
Upvote 0
Thử thêm 2 cái ByRef cho nó nhé:

Mã:
Function HoangTrongNghia([B][COLOR=#ff0000]ByRef [/COLOR]k As Long, [COLOR=#ff0000]ByRef [/COLOR]Res()[/B])
    k = k + 1
    ReDim Preserve Res(1 To k)
    Res(k) = k
End Function

''---------------------------------------------------


Sub Test()
[B]    [COLOR=#ff0000]Static [/COLOR]k As Long, Res()[/B]
    HoangTrongNghia k, Res
    MsgBox k & "/" & UBound(Res)
End Sub

Bấm code Test vài lần xem sao!
 
Upvote 0
[h=2]Làm sao để không phải khai báo biến toàn cục trong code này[/h]

Tôi cũng dùng biến toàn cục nhưng chỉ 1 biến thôi. Tôi dùng Dictionary làm biến toàn cục, mọi thứ nạp vào nó
Tuy nhiên, đã test nhiều lần rồi, FileSystemObject dùng để lấy list file, folder không kham nỗi khi phải lấy vài chục ngàn đến vài trăm ngàn file ---> Nó chậm lết bánh luôn. Chỉ có DOS mới là vô địch trong vụ này
 
Upvote 0
Các bạn lưu ý, với Mảng được khai báo là đối số trong Function hay trong Sub thì có ByRef hay không cũng ngầm hiểu là ByRef nha các bạn, các bạn không thể khai ByVal với mảng được!

Mã:
[COLOR=#000000]Function HoangTrongNghia([/COLOR][B][COLOR=#ff0000]ByRef [/COLOR]k As Long, [COLOR=#ff0000]ByRef [/COLOR]Res()[/B][COLOR=#000000])[/COLOR]

Hoặc:

Mã:
[COLOR=#000000]Function HoangTrongNghia([/COLOR][B][COLOR=#ff0000]ByRef [/COLOR]k As Long, Res()[/B][COLOR=#000000])[/COLOR]

Đều OK.
 
Upvote 0
Mình có viết đoạn code để lấy tất cả file trong thư mục mẹ và thư mục con.
Nhưng loay hoay mãi không biết làm sao để không phải khai báo biến toàn cục.
Các anh chị xem qua code và vui lòng xin cho ý kiến.
PHP:
Public k As Long, Res()
Sub Main()
Dim strPath As String, fso As Object, chk As Boolean
Set fso = CreateObject("Scripting.FileSystemObject")
   With Application.FileDialog(msoFileDialogFolderPicker)
      chk = .Show
      If Not chk Then Exit Sub
      strPath = .SelectedItems(1)
   End With
   If Len(strPath) Then Call GetAllFiles(strPath, fso)
   If k Then [A5].Resize(k) = Application.Transpose(Res)
   k = 0
End Sub
Function GetAllFiles(ByVal StrFolder As String, ByRef fso As Object)
Dim objFolder As Object, objSubFolder As Object, File
Set objFolder = fso.GetFolder(StrFolder)
For Each objSubFolder In objFolder.SubFolders
   Call GetAllFiles(objSubFolder.path, fso)
   For Each File In objSubFolder.Files
      k = k + 1
      ReDim Preserve Res(1 To k)
      Res(k) = fso.GetBaseName(File)
   Next
Next objSubFolder
End Function

For Each File In objSubFolder.Files


Nhìn vào chỗ đỏ đỏ thì thấy bạn chỉ liệt kê những tập tin có trong những thư mục con. Như vậy bạn mất những tập tin có trong thư mục được lựa chọn. Vd. trong thư mục hichic có 100 tập tin từ f1.txt tới f100.txt, và k thư mục con (k >= 0) thì sau khi chạy code của bạn và chọn thư mục hichic thì bạn không có các kết quả từ f1.txt tới f100.txt

Code của bạn, tôi chỉ "sắp xếp" lại

Mã:
Sub Main()
Dim fso As Object, res() As String
    Set fso = CreateObject("Scripting.FileSystemObject")
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show Then
            ReDim res(1 To 1)
            GetAllFiles .SelectedItems(1), fso, res
            If UBound(res) > 1 Then [A5].Resize(UBound(res) - 1) = Application.Transpose(res)
        End If
    End With
End Sub

Function GetAllFiles(ByVal StrFolder As String, fso As Object, res() As String)
Dim objFolder As Object, objSubFolder As Object, File
    Set objFolder = fso.GetFolder(StrFolder)
    For Each objSubFolder In objFolder.SubFolders
        Call GetAllFiles(objSubFolder.Path, fso, res)
        For Each File In objSubFolder.Files
            res(UBound(res)) = fso.GetBaseName(File)
            ReDim Preserve res(1 To UBound(res) + 1)
        Next
    Next objSubFolder
End Function

Để liệt kê hết tôi đề nghị - mới test 1 lần, không chịu trách nhiệm nếu sảy ra thiệt hại ;-)

Mã:
Function GetAllFiles(ByVal StrFolder As String, fso As Object, res() As String)
Dim objFolder As Object, objSubFolder As Object, File
    Set objFolder = fso.GetFolder(StrFolder)
    For Each File In objFolder.Files
        res(UBound(res)) = fso.GetBaseName(File)
        ReDim Preserve res(1 To UBound(res) + 1)
    Next
    For Each objSubFolder In objFolder.SubFolders
        GetAllFiles objSubFolder.Path, fso, res
    Next objSubFolder
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
[/COLOR][/COLOR]
Nhìn vào chỗ đỏ đỏ thì thấy bạn chỉ liệt kê những tập tin có trong những thư mục con. Như vậy bạn mất những tập tin có trong thư mục được lựa chọn. Vd. trong thư mục hichic có 100 tập tin từ f1.txt tới f100.txt, và k thư mục con (k >= 0) thì sau khi chạy code của bạn và chọn thư mục hichic thì bạn không có các kết quả từ f1.txt tới f100.txt

Code của bạn, tôi chỉ "sắp xếp" lại

Mã:
Sub Main()
Dim fso As Object, res() As String
    Set fso = CreateObject("Scripting.FileSystemObject")
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show Then
            ReDim res(1 To 1)
            GetAllFiles .SelectedItems(1), fso, res
            If UBound(res) > 1 Then [A5].Resize(UBound(res) - 1) = Application.Transpose(res)
        End If
    End With
End Sub

Function GetAllFiles(ByVal StrFolder As String, fso As Object, res() As String)
Dim objFolder As Object, objSubFolder As Object, File
    Set objFolder = fso.GetFolder(StrFolder)
    For Each objSubFolder In objFolder.SubFolders
        Call GetAllFiles(objSubFolder.Path, fso, res)
        For Each File In objSubFolder.Files
            res(UBound(res)) = fso.GetBaseName(File)
            ReDim Preserve res(1 To UBound(res) + 1)
        Next
    Next objSubFolder
End Function

Để liệt kê hết tôi đề nghị - mới test 1 lần, không chịu trách nhiệm nếu sảy ra thiệt hại ;-)

Mã:
Function GetAllFiles(ByVal StrFolder As String, fso As Object, res() As String)
Dim objFolder As Object, objSubFolder As Object, File
    Set objFolder = fso.GetFolder(StrFolder)
    For Each File In objFolder.Files
        res(UBound(res)) = fso.GetBaseName(File)
        ReDim Preserve res(1 To UBound(res) + 1)
    Next
    For Each objSubFolder In objFolder.SubFolders
        GetAllFiles objSubFolder.Path, fso, res
    Next objSubFolder
End Function
Chạy tốt rồi anh. Lại học thêm được 1 cái hay từ anh. Vậy mà em nghĩ nát óc không ra.
 
Upvote 0
Tôi cũng dùng biến toàn cục nhưng chỉ 1 biến thôi. Tôi dùng Dictionary làm biến toàn cục, mọi thứ nạp vào nó
Tuy nhiên, đã test nhiều lần rồi, FileSystemObject dùng để lấy list file, folder không kham nỗi khi phải lấy vài chục ngàn đến vài trăm ngàn file ---> Nó chậm lết bánh luôn. Chỉ có DOS mới là vô địch trong vụ này

Em cũng có học được cách lấy file bằng Dos từ code của anh rồi, đúng là nhanh lắm. Nhưng tại muốn thử sức mình với FileSystemObject xem thế nào. Thật ra nghiên cứu cho thỏa mản thôi chứ có bao giờ xài tới đâu.
 
Upvote 0
Em cũng có học được cách lấy file bằng Dos từ code của anh rồi, đúng là nhanh lắm. Nhưng tại muốn thử sức mình với FileSystemObject xem thế nào. Thật ra nghiên cứu cho thỏa mản thôi chứ có bao giờ xài tới đâu.

Với trình độ của bạn đâu có cần phải "thử sức" với ba cái công cụ của Script.

Nếu tôi là bạn, tôi chú trọng "thử sức" kỹ thuật hàm đệ quy.
(thông thường thì hàm đệ quy tránh dùng biến toàn cục, và biến tĩnh (static) lại càng tránh hơn - tôi chỉ nói tránh thôi, nếu cực chẳng đã thì vẫn phải dùng)
 
Upvote 0
Với trình độ của bạn đâu có cần phải "thử sức" với ba cái công cụ của Script.

Nếu tôi là bạn, tôi chú trọng "thử sức" kỹ thuật hàm đệ quy.
(thông thường thì hàm đệ quy tránh dùng biến toàn cục, và biến tĩnh (static) lại càng tránh hơn - tôi chỉ nói tránh thôi, nếu cực chẳng đã thì vẫn phải dùng)
Mình có qua trường lớp gì đâu, toàn là lấy code của anh em trên diễn đàn về nghiên cứu thuật toán, rồi thêm bớt sửa qua sửa lại. Sau 2 năm vọc phá thì chỉ có được chút ít kiến thức căn bản. Giờ anh nói hàm đệ quy gì đó, thật tình mình chẳng biết nó là cái gì, và phải bắt đầu từ đâu.
 
Upvote 0
Đệ quy (recursive) có hai loại:

1. trực tiếp tức là hàm gọi chính nó (code của bạn sử dụng loại này)

2. gián tiếp tức là hàm A gọi B và B gọi A, hoặc A gọi B, B gọi C và C gọi A; chéo cẳng ngỗng tùm lum. loại này khó giàn trời.

Vì loại 2 rất khó nên thường người ta chỉ nói đến loại 1.
Loại này còn chia ra nhiều kiểu nhỏ là trực tuyên, phi tuyến, (còn mấy loại nữa tôi quên mất tên)

Hầu hết các code vòng lặp đều có thể đổi thành hàm đệ quy. Nếu bạn muốn luyện thì cứ đè chỗ đó ra mà luyện.

Mẹo: khi viết code đệ quy thì chú trọng đến điểm thoát. Tức là có một thông số nào đó để hàm quay về không gọi tiếp nữa. Nếu điểm thoát không được chú trọng kỹ, code có thể chạy hoài và crash (khác với vòng lặp, code đệ quy tốn bộ nhớ cho nên chạy một chốc sẽ bị hết bộ nhớ và crash)

Ví dụ hàm tính giai thừa:
Function GT(byVal so As Long) As Long
If so <= 0 then
GT = 1 ' đây là điểm thoát, không gọi tiếp nữa
Else
GT = so * GT(so-1)
End If
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
[/COLOR][/COLOR]
Nhìn vào chỗ đỏ đỏ thì thấy bạn chỉ liệt kê những tập tin có trong những thư mục con. Như vậy bạn mất những tập tin có trong thư mục được lựa chọn. Vd. trong thư mục hichic có 100 tập tin từ f1.txt tới f100.txt, và k thư mục con (k >= 0) thì sau khi chạy code của bạn và chọn thư mục hichic thì bạn không có các kết quả từ f1.txt tới f100.txt

Code của bạn, tôi chỉ "sắp xếp" lại

Mã:
Sub Main()
Dim fso As Object, res() As String
    Set fso = CreateObject("Scripting.FileSystemObject")
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show Then
            ReDim res(1 To 1)
            GetAllFiles .SelectedItems(1), fso, res
            If UBound(res) > 1 Then [A5].Resize(UBound(res) - 1) = Application.Transpose(res)
        End If
    End With
End Sub

Function GetAllFiles(ByVal StrFolder As String, fso As Object, res() As String)
Dim objFolder As Object, objSubFolder As Object, File
    Set objFolder = fso.GetFolder(StrFolder)
    For Each objSubFolder In objFolder.SubFolders
        Call GetAllFiles(objSubFolder.Path, fso, res)
        For Each File In objSubFolder.Files
            res(UBound(res)) = fso.GetBaseName(File)
            ReDim Preserve res(1 To UBound(res) + 1)
        Next
    Next objSubFolder
End Function

Để liệt kê hết tôi đề nghị - mới test 1 lần, không chịu trách nhiệm nếu sảy ra thiệt hại ;-)

Mã:
Function GetAllFiles(ByVal StrFolder As String, fso As Object, res() As String)
Dim objFolder As Object, objSubFolder As Object, File
    Set objFolder = fso.GetFolder(StrFolder)
    For Each File In objFolder.Files
        res(UBound(res)) = fso.GetBaseName(File)
        ReDim Preserve res(1 To UBound(res) + 1)
    Next
    For Each objSubFolder In objFolder.SubFolders
        GetAllFiles objSubFolder.Path, fso, res
    Next objSubFolder
End Function
Em xin mượn gió bẻ măng. Vụ chế tác em cũng thuộc hạng ghê thiệt. Nhiều lúc tự phục mình sát đất.
PHP:
Sub Main()
Dim fso As Object, Dic As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set Dic = CreateObject("scripting.dictionary")
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show Then
            GetAllFiles .SelectedItems(1), fso, Dic
            [A5].Resize(Dic.Count) = Application.Transpose(Dic.Keys)
        End If
    End With
End Sub
Function GetAllFiles(ByVal StrFolder As String, fso As Object, Dic As Object)
Dim objFolder As Object, objSubFolder As Object, File As Object
    Set objFolder = fso.GetFolder(StrFolder)
    For Each File In objFolder.Files
        Dic.Item(File.Name) = ""
    Next
    For Each objSubFolder In objFolder.SubFolders
        GetAllFiles objSubFolder.path, fso, Dic
    Next objSubFolder
End Function
 
Upvote 0
Em xin mượn gió bẻ măng. Vụ chế tác em cũng thuộc hạng ghê thiệt. Nhiều lúc tự phục mình sát đất.
PHP:
Sub Main()
Dim fso As Object, Dic As Object
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set Dic = CreateObject("scripting.dictionary")
    With Application.FileDialog(msoFileDialogFolderPicker)
        If .Show Then
            GetAllFiles .SelectedItems(1), fso, Dic
            [A5].Resize(Dic.Count) = Application.Transpose(Dic.Keys)
        End If
    End With
End Sub
Function GetAllFiles(ByVal StrFolder As String, fso As Object, Dic As Object)
Dim objFolder As Object, objSubFolder As Object, File As Object
    Set objFolder = fso.GetFolder(StrFolder)
    For Each File In objFolder.Files
        Dic.Item(File.Name) = ""
    Next
    For Each objSubFolder In objFolder.SubFolders
        GetAllFiles objSubFolder.path, fso, Dic
    Next objSubFolder
End Function
code này Nếu mình thích lấy thêm đường dẫn của file nữa thì sữa thế nào anh
thanks Anh
 
Upvote 0
code này Nếu mình thích lấy thêm đường dẫn của file nữa thì sữa thế nào anh
thanks Anh
Sửa lại chỗ này
PHP:
    For Each File In objFolder.Files
        Dic.Item(fso.GetAbsolutePathName(File)) = ""
    Next
Thật ra thì phải nên có đường dẫn mới đúng, nếu không sẽ có thể bị thiếu file nếu có file trùng tên
 
Lần chỉnh sửa cuối:
Upvote 0
Dùng Dictionary ở đây có vẻ lạm phát. Theo tôi thì nên dùng ArrayList. Nếu kết quả muốn theo thứ tự từng lớp (tầng folder) thì cũng dễ sort.
 
Upvote 0
Dùng Dictionary ở đây có vẻ lạm phát. Theo tôi thì nên dùng ArrayList. Nếu kết quả muốn theo thứ tự từng lớp (tầng folder) thì cũng dễ sort.
Dùng ArrayList sẽ có máy không xài được vì thiếu NET hay gì đó. Thật ra chỉ nghiên cứu chơi thôi, chứ nếu phải lấy toàn bộ file thì mình sẽ kết hợp FSO và DOS cho nhanh và cũng đơn giản.
 
Upvote 0
Sửa lại chỗ này
PHP:
    For Each File In objFolder.Files
        Dic.Item(fso.GetAbsolutePathName(File)) = ""
    Next
Thật ra thì phải nên có đường dẫn mới đúng, nếu không sẽ có thể bị thiếu file nếu có file trùng tên

File.Name là để lấy tên file
File.Path là lấy đường dẫn đầy đủ đến file
-----------------------------
Thật ra chỉ nghiên cứu chơi thôi, chứ nếu phải lấy toàn bộ file thì mình sẽ kết hợp FSO và DOS cho nhanh và cũng đơn giản.
Khi ấy FSO chỉ làm công việc đọc file TEXT mà thôi
 
Upvote 0
Web KT
Back
Top Bottom