Lấy nội dung file INI bằng VBA-Hàm GetPrivateProfileString

Trạng thái
Không mở trả lời sau này.

Euler

Mod
Thành viên BQT
1. File INI là gì? Ví dụ về file INI
2. Lấy giá trị bằng Key chỉ định thông qua hàm GetPrivateProfileString.

Chức năng của hàm GetPrivateProfileString.
3. Code ví dụ
4. Trích xuất một thành phần từ giá trị lấy được
5. Những vấn đề xung quanh hàm GetPrivateProfileString

 

Euler

Mod
Thành viên BQT
1. File INI là gì? Ví dụ về file INI
Cũng có trường hợp chúng ta phải lưu thông tin thiết định bên ngoài file excel. Như vậy VBA phải can thiệp ra bên ngoài. Trong trường hợp này ta có thể sử dụng file INI.
Microsoft thường khuyến khích chúng ta sử dụng XML hoặc registry chứ không phải INI.
Tuy nhiên hiện nay việc sử dụng file INI cũng đang nhiều lên rồi.

Cấu trúc bên trong file INI:
Mã:
; Comment sau dấu chấm phẩy
[Section]
key=value
Tổng quát:
Mã:
;Định dạng của nội dung trong tập tin ini như sau:
[Section1]
Key1= Value1
Key2= Value2
...
'[Section2]
Key3= Value3
Key4= Value4
Chú ý:
  • Section phải được bao bởi dấu ngoặc vuông [ ] .Ngay sau Section là các key và value thuộc Section đó.
  • Các Section khác nhau mà trùng tên key thì cũng không sao.
Ví dụ về file INI:
Mã:
;//------------------------------------------
;// File Name:tcp.ini
;// Nội dung      :Thiết định cần thiết cho xử lý TCP
;//------------------------------------------
;// Thông tin TCP Sever
[TCP_SERVER]
IP=192.168.20.1
PORT=12345
;// Thông tin TCP client
[TCP_CLIENT]
IP=192.168.11.5
PORT=50005
Mình tham khảo rất nhiều nguồn, thực sự không phải cứ người Nhật viết là dễ hiểu đâu, đây là bài viết mà mình thấy dễ hiểu nhất, kết hợp thêm mấy nguồn phụ khác mới được bài viết dễ hiểu như thế này đấy, mọi người hãy trân trọng công sức của mình và Like nhé ^_^
 
Sửa lần cuối:

giaiphapvba

Administrator
Thành viên BQT
2. Lấy giá trị bằng Key chỉ định thông qua hàm GetPrivateProfileString.
Chức năng của hàm GetPrivateProfileString.
Thông thường nếu là file text (txt), chúng ta có thể sử dụng để lấy thông tin.
Nhưng đối với file INI, chúng ta sẽ sử dụng hàm GetPrivateProfileString (WIN32 API - được phát triển từ WIN16) để lấy thông tin mà không cần tiến hành thao tác với file như đã nói ở trên (không sử dụng FSO).

Chức năng của hàm GetPrivateProfileString.
Hàm GetPrivateProfileString sẽ tìm kiếm Key (Tham số lpKeyName: tên từ khóa mà bạn chỉ định cho hàm) trong file INI dựa vào Section (Tham số lpApplicationName: bạn chỉ định cho hàm) và lấy giá trị Value tương ứng với Key tìm được đó.

Ở đây tôi đang nói trong trường hợp tìm thấy từ khóa, thì nó sẽ lấy giá trị tuwong ứng với key đó trả về tham số thứ 4 của hàm.

Trường hợp không tìm thấy key, thì nó sẽ trả về tham số thứ 4 giá trị mà bạn thiết định cho tham số thứ 3 (lpDefault).

Cấu trúc của hàm sẽ như dưới đây:
Mã:
Function GetPrivateProfileString( _
    ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, _
    ByVal lpDefault As String, _
    ByVal lpReturnedString As String, _
    ByVal nSize As Long, _
    ByVal lpFileName As String
) As Long
lpApplicationNameChỉ định Section <Tìm kiếm trong Section nào>
lpKeyNameChỉ định từ khóa tìm kiếm trong vùng Section đã chỉ định ở trên
lpDefaultGiá trị của hàm khi không tìm thấy từ khóa
lpReturnedStringNếu tìm thấy từ khóa thì sẽ thiết định giá trị cho tham số này.
Chú ý kích thước vùng lưu trữ chuỗi ký tự tìm thấy là quan trọng. Nếu kích thước vùng lưu trữ không đủ, có thể không lấy được giá trị tìm thấy.
nSizeVì lý do trên, ta phải chỉ định kích thước cho lpReturnedString .
lpFileName Đường dẫn file INI
Giá trị trả vềSố bye của chuỗi ký tự lấy được.
 

Euler

Mod
Thành viên BQT
3. Code ví dụ
Chúng ta sẽ viết code để lấy thông tin .
Để sử dụng hàm API a các bạn cần sử dụng câu lệnh Declare. Đối với máy tính win64 VBA7 thì cần thêm từ khóa PtrSafe vào trước tên Function ...
Chúng ta cần phải chuẩn bị không gian để lưu trữ giá trị thu được. Việc này cũng tương tự như chúng ta cần có không gian trống để có chỗ cho một vị khách tới trọ. Do đó chúng ta sẽ sử dụng hàm Space.
Giá trị lấy được có thể chứa các khoảng trống ở trước và sau, do chỉ muốn lấy giá trị cho nên ta sẽ sử dụng hàm TRIM. Tuy nhiên việc này không loại bỏ được ký tự NULL (thường là ký tự cuối cùng kết thúc chuỗi ký tự).
Ký tự NULL là ký tự có mã ASCII là 0, trong VBA ta viết Chr(0) hiểu đó là ký tự NULL.
Tôi ví dụ:
INPUT: sValue
OUTPUT: sValue2
Quá trình xử lý kết quả sẽ tiến hành như sau:
Bạn cần đăng nhập để thấy đính kèm

Mã:
Declare PtrSafe Function GetPrivateProfileString Lib _
    "kernel32" Alias "GetPrivateProfileStringA" ( _
    ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, _
    ByVal lpDefault As String, _
    ByVal lpReturnedString As String, _
    ByVal nSize As Long, _
    ByVal lpFileName As String _
) As Long

Sub GetPrivateProfileStringTest()
    Dim sPath                   '// Đường dẫn file INI
    Dim sValue      As String   '// Giá trị lấy được sẽ lưu vào đây
    Dim lSize       As Long     '// Size của giá trị lấy về
    Dim lRet        As Long     '// Giá trị trả về
    
    lSize = 20
    sPath = "C:\test\tcp.ini"

    '// Khởi tạo bộ nhớ đệm thu được
    sValue = Space(lSize)
    '// Lấy giá trị của Key "IP" của Section [TCP_SERVER]
    lRet = GetPrivateProfileString("TCP_SERVER", "IP", "non", sValue, lSize, sPath)
    Debug.Print "[" & sValue & "]"
    Debug.Print "[" & Trim(sValue) & "]"
    Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]"
    
    '// Khởi tạo bộ nhớ đệm thu được
    sValue = Space(lSize)
    '// Lấy giá trị của key "PORT" của Section [TCP_CLIENT]
    lRet = GetPrivateProfileString("TCP_CLIENT", "PORT", "non", sValue, lSize, sPath)
    Debug.Print "[" & sValue & "]"
    Debug.Print "[" & Trim(sValue) & "]"
    Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]"
End Sub
Kết quả:
Mã:
[192.168.20.1        ]
[192.168.20.1 ]
[192.168.20.1]
[192.168.20.1]
[50005               ]
[50005 ]
[50005]
[50005]
 

tuhocvba

Administrator
Thành viên BQT
4. Trích xuất một thành phần từ giá trị lấy được
Phần này nói về việc ký tự mà các bạn lấy được sẽ bao gồm những thứ như rác, chẳng hạn ký tự NULL, hay là khoảng trống-đó là những thứ ta không cần lấy.
Ví dụ ký tự ban đầu lấy được là:
Mã:
[192.168.20.1        ]
Nhưng kết quả cuối cùng ta muốn lấy là:
Mã:
[192.168.20.1]
Do đó chúng ta sẽ dùng hàm INSTR để tìm kiếm kí tự NULL - chr(0) và sau đó dùng hàm LEFT để lấy phần ký tự trước NULL, sử dụng hàm TRIM để loại bỏ khoảng trống ta sẽ thu được kết quả cuối cùng cần lấy.
@NhanSu : Cảm ơn bạn đã đóng góp ý kiến. Tuy nhiên chúng tôi lo lắng rằng việc đó sẽ tạo nên ngắt quãng suy nghĩ không cần thiết. Vậy mong bạn hợp tác và chờ tới khi chúng tôi dịch tới phần cuối (phần 5), rồi sau đó chúng ta sẽ thảo luận sau. Tạm thời bài của bạn, chúng tôi sẽ bảo lưu, nhưng sau đó sẽ copy đặt ở vị trí thích hợp, bài cũ sẽ xóa.
 

Euler

Mod
Thành viên BQT
5. Những vấn đề xung quanh hàm GetPrivateProfileString
Ở đoạn code mẫu , tôi đã viết một chút chú thích. Và ở phần này tôi muốn nói rõ hơn những trở ngại khi dùng hàm GetPrivateProfileString.

Trở ngại đầu tiên là chúng ta phải khai báo Declare. Điều này thì cũng giống như các hàm API khác, ừ thì tạm thời cũng không khó khăn gì lắm.

Trở ngại thứ hai là việc lấy giá trị trả về thật là rắc rối. Trước đó nào là phải chuẩn bị vùng trống để lưu giá trị, rồi sau khi lấy được lại phải gọt tỉa các phần dư thừa không cần thiết. Thật là tốn quá nhiều công sức.

Trở ngại thứ ba, đó là tốc độ xử lý chậm. Cứ mỗi lần gọi hàm này, mặc dù chúng ta không thể nhìn được bằng mắt, tuy nhiên mỗi lần hàm GetPrivateProfileString thực thi, nó sẽ mở file ini và sau đó đóng file ini.
Nếu việc lấy giá trị ít thì không sao, nhưng nhiều thì sẽ là nguyên nhân làm cho chương trình chạy chậm.

Cuối cùng là vấn đề thứ tư, hạn chế ký tự trong file INI là nó chỉ chấp nhận mã ký tự Shift-JIS (có thể tham khảo mã ký tự ). Trong khi đó việc sử dụng UTF-8 đang ngày càng tăng, cứ suy nghĩ tới điều đó thôi đã thấy phiền hà rồi. Cho nên các bạn cần cân nhắc.
Lời người dịch: Nếu chỉ đơn thuần lưu tên biến và giá trị, sử dụng cách viết không dấu-mình nghĩ không có vấn đề gì, đây không là trở ngại.
(Hết)
=========================================
Trong khuôn khổ topic này chỉ bàn về việc lấy thông tin trong file INI. Về việc ghi thông tin vào file INI, chúng tôi sẽ có một topic khác.

Về ý kiến của bạn @NhanSu :
Vì biến lret chứa độ dài của chuỗi trả về nên có thể dùng left(svalue,lret) để lấy chuỗi. Nếu dùng Trim có thể gây ra thay đổi chuỗi ban đầu trong file INI.
Đúng, nếu bạn biết chính xác độ dài chuỗi ký tự cần lấy thì khi đó trích xuất phần giá trị của svalue sẽ không cần các xử lý phức tạp ở bài viết trước.
 
M

maiban2068

Guest
Vì biến lret chứa độ dài của chuỗi trả về nên có thể dùng left(svalue,lret) để lấy chuỗi.
Nếu biết trước độ dài cần lấy, thì tôi nghĩ cũng không cần dùng LEFT như bạn nói, mà có thể dùng trực tiếp trong khai báo.
Ví dụ:
Mã:
Sub vidu()
    Dim s As String * 2 '2 là số ký tự. Không phải là số byte
    s = "abc"
    MsgBox s
End Sub
Kết quả:
Bạn cần đăng nhập để thấy hình ảnh

Hay là tôi hiểu nhầm ý bạn nhỉ. Mong được chỉ giáo.
 

NhanSu

Thành Viên Nổi Bật

@maiban2068 biến lret là kết quả trả về sau khi gọi hàm. Trước khi gọi ta chưa biết độ dài bao nhiêu nên phải tạo bộ đệm đủ lớn. Khi gọi hàm thì hàm trả về kết quả là độ dài thực sự của chuỗi nên chỉ cần dùng left.
 
M

maiban2068

Guest
@maiban2068 biến lret là kết quả trả về sau khi gọi hàm. Trước khi gọi ta chưa biết độ dài bao nhiêu nên phải tạo bộ đệm đủ lớn. Khi gọi hàm thì hàm trả về kết quả là độ dài thực sự của chuỗi nên chỉ cần dùng left.
Bạn đưa hẳn code ra, tôi không hiểu bạn đang nói cái gì.
 

NhanSu

Thành Viên Nổi Bật

@maiban2068 Code trên bài 4 của bạn Euler, dòng 26
Mã:
Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]"
Ví dụ chuỗi trong file INI là "a@@@@@@@@b" (10 ký tự) thì sau khi gọi hàm, svalue="a@@@@@@@@b@@@@@@@@@@" (20 ký tự) và lret=10.
Nếu dùng code trên thì kết quả chuỗi sẽ thành "a@b", đây không phải là chuỗi đúng. Khi dùng
Mã:
Debug.Print left(svalue,lret)
sẽ trả về chuỗi giống trong file INI là "a@@@@@@@@b".
(Các ký tự @ thay cho khoảng trắng do diễn đàn tự động TRIM chuỗi có khoảng trắng)
 
M

maiban2068

Guest
Tôi không hiểu.
Bạn gửi tôi file ini của bạn và chương trình hoàn thiện của bạn, tôi test thử xem sao. Bạn gặp khó khăn trong diễn đạt à?
 

NhanSu

Thành Viên Nổi Bật

Tôi không có file nào cả, tôi chỉ đóng góp bổ sung ý kiến và code của bạn @Euler đó là thay dòng 26 trong code bài 4 thành
Mã:
Debug.Print left(svalue,lret)
sẽ dễ viết hơn và chính xác hơn.
 

tuhocvba

Administrator
Thành viên BQT
Các bạn chú ý, đưa ra vấn đề cần có dẫn chứng, trong trường hợp cần file demo, hãy đưa file demo, để người khác nhanh chóng kiểm nghiệm.
Trường hợp nói vắn tắt có thể dẫn tới ý hiểu khác. Vì vậy hãy nói đầy đủ, đưa ra đoạn code đầy đủ để mọi người kiểm nghiệm.
Tôi không đồng ý không khí thảo luận như thế này.
 
M

maiban2068

Guest
Tôi không có file nào cả, tôi chỉ đóng góp bổ sung ý kiến và code của bạn @Euler đó là thay dòng 26 trong code bài 4 thành
Mã:
Debug.Print left(svalue,lret)
sẽ dễ viết hơn và chính xác hơn.
Bạn không tự tạo một cái file ini theo ý bạn được à?
Tôi có cần dòng code nào đâu, tôi cần cả chương trình của bạn.
Việc đó khó khăn với bạn à?
 

NhanSu

Thành Viên Nổi Bật

Ví dụ
Mã:
Option Explicit

Declare PtrSafe Function GetPrivateProfileString Lib _
    "kernel32" Alias "GetPrivateProfileStringA" ( _
    ByVal lpApplicationName As String, _
    ByVal lpKeyName As Any, _
    ByVal lpDefault As String, _
    ByVal lpReturnedString As String, _
    ByVal nSize As Long, _
    ByVal lpFileName As String _
) As Long

Sub GetPrivateProfileStringTest()
    Dim sPath                   '// Ðu?ng d?n file INI
    Dim sValue      As String   '// Giá tr? l?y du?c s? luu vào dây
    Dim lSize       As Long     '// Size c?a giá tr? l?y v?
    Dim lRet        As Long     '// Giá tr? tr? v?
    
    lSize = 20
    sPath = ThisWorkbook.Path & "\abc.ini"

    '// Kh?i t?o b? nh? d?m thu du?c
    sValue = Space(lSize)
    '// L?y giá tr? c?a Key "IP" c?a Section [TCP_SERVER]
    lRet = GetPrivateProfileString("test", "key1", "non", sValue, lSize, sPath)
    
    'Euler' code
    Debug.Print "[" & sValue & "]"
    Debug.Print "[" & Trim(sValue) & "]"
    Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]"
    'my code
    Debug.Print "[" & Left(sValue, lRet) & "]"
    
End Sub
 
M

maiban2068

Guest
Cảm ơn bạn. Đã dễ hiểu hơn.
Ví dụ
Mã:
Option Explicit



Declare PtrSafe Function GetPrivateProfileString Lib _

    "kernel32" Alias "GetPrivateProfileStringA" ( _

    ByVal lpApplicationName As String, _

    ByVal lpKeyName As Any, _

    ByVal lpDefault As String, _

    ByVal lpReturnedString As String, _

    ByVal nSize As Long, _

    ByVal lpFileName As String _

) As Long



Sub GetPrivateProfileStringTest()

    Dim sPath                   '// Ðu?ng d?n file INI

    Dim sValue      As String   '// Giá tr? l?y du?c s? luu vào dây

    Dim lSize       As Long     '// Size c?a giá tr? l?y v?

    Dim lRet        As Long     '// Giá tr? tr? v?

  

    lSize = 20

    sPath = ThisWorkbook.Path & "\abc.ini"



    '// Kh?i t?o b? nh? d?m thu du?c

    sValue = Space(lSize)

    '// L?y giá tr? c?a Key "IP" c?a Section [TCP_SERVER]

    lRet = GetPrivateProfileString("test", "key1", "non", sValue, lSize, sPath) 
    'Euler' code
    Debug.Print "[" & sValue & "]"
    Debug.Print "[" & Trim(sValue) & "]"
    Debug.Print "[" & Trim(Left(sValue, InStr(sValue, Chr(0)) - 1)) & "]"

    'my code
    Debug.Print "[" & Left(sValue, lRet) & "]"
  
End Sub
Kết quả:
Mã:
[a           b       ]
[a           b ]
[a           b]
[a           b]
Dễ hiểu hơn rồi đấy.
Bây giờ tôi sửa lại nội dung INI của bạn.
Link download ở đây, bạn test nhé:
Kết quả:
Mã:
[a           b      ]
[a           b   ]
[a           b]
[a           b   ]
Kết quả của bạn là cái dòng cuối cùng ấy.
 

NhanSu

Thành Viên Nổi Bật

Có gì sai đâu nhỉ?
Bạn cần đăng nhập để thấy đa phương tiện
 
M

maiban2068

Guest
Có gì sai đâu nhỉ?
Bạn cần đăng nhập để thấy đa phương tiện
Người ta muốn kết quả như thế này:
Mã:
[a           b]
Còn của bạn ra như thế này:
Mã:
[a           b   ]
 

sieutocviet3

Thành viên
Có gì sai đâu nhỉ?
Bạn cần đăng nhập để thấy đa phương tiện
Cái khoảng trắng người ta cố tình cài bạn nó có mã ascii là :
Bạn cần đăng nhập để thấy hình ảnh

Bạn cần đăng nhập để thấy hình ảnh

Mã:
Sub aaa()
    MsgBox Asc(" ")
End Sub
 

NhanSu

Thành Viên Nổi Bật

Bạn nhìn kỹ hình ảnh kết quả của tôi ở dòng dưới có giống với file INI không. Khi đọc file INI cần chuỗi trả về phải giống với file gốc, tuy vậy hàm GetPrivateProfileString đã tự động TRIM chuỗi đọc được, cắt các khoảng trắng ở 2 đầu.
 
Trạng thái
Không mở trả lời sau này.
Top