Kiểm tra sự tồn tại của một sheet khi không mở file (1 người xem)

  • Thread starter Thread starter nncb2008
  • Ngày gửi Ngày gửi
Liên hệ QC

Người dùng đang xem chủ đề này

nncb2008

Thành viên chính thức
Tham gia
14/2/08
Bài viết
88
Được thích
3
Chào các anh chị trên diễn đàn.
Tôi có điều cần trợ giúp: Muốn kiểm tra sheetname có tồn tại trong filename.xls ở thư mục C:\MYFOLDER mà không mở file.
Xin cảm ơn.
 
Chào các anh chị trên diễn đàn.
Tôi có điều cần trợ giúp: Muốn kiểm tra sheetname có tồn tại trong filename.xls ở thư mục C:\MYFOLDER mà không mở file.
Xin cảm ơn.
Mở 1 file exxcel bất kỳ. gõ " =C:\MYFOLDER\[filename]Sheetname'!$B$4"
Nếu sheet không có Excel sẽ hiện bảng báo update hơạc báo "#REF"
 
Upvote 0
On Error Resume Next
str = Dir$(<tên file>)
If str = vbNullString Then
'không tồn tại
Else
'tồn tại
End If
 
Upvote 0
Chào các anh chị trên diễn đàn.
Tôi có điều cần trợ giúp: Muốn kiểm tra sheetname có tồn tại trong filename.xls ở thư mục C:\MYFOLDER mà không mở file.
Xin cảm ơn.

Xin hỏi bạn : tại sao phải yêu cầu " không mở file " --> có nhất thiết phải như vậy không ?
Hay chỉ cần đơn giản :
PHP:
Workbooks.Open " đường dẫn"
For ws in worksheets
   If ws.name = "..." then
next
Workbooks.close(..)
Còn nếu bắt buộc không mở file theo cách bạn muốn --> vọc thêm về topic ADO trên diễn đàn
 
Upvote 0
Thực ra yêu cầu của tác giả là bất khả thi... trừ khi chúng ta có phép, mà có phép thì vẫn cứ phải mở file - không bằng cách này thì cách khác.
Muốn biết trong túi có gì thì: (1) Mở miệng túi ra (2) Dùng máy Soi (vẫn phải mở nhỉ) (3) Thò tay vào túi.
Muốn biết sheet nào đó có tồn tại trong tập tin excel không thì chả có cách nào khác ngoài việc phải mở nó ra. Có chăng thì mở bằng chương trình nào mà thôi.
Trong phạm vi hỏi của tác giả, thì tôi đoán không phải dùng các biện pháp quá cao siêu như dùng trình soạn thảo khác Excel để mở mà có thể dùng Excel mở nó ra, kiểm tra xong không có thì đóng (tất nhiên còn nhiều công cụ khác nữa).
Để làm được yêu cầu của tác giả thì chắc dùng VBA (lập trình) trong chính Excel thôi. Nếu tác giả lại nói là không muốn mở Excel ra thì chắc phải áp dụng các giải pháp cao siêu khác rồi...Trong trường hợp này, tác giả cần đưa ra yêu cầu cụ thể hơn...
Xin có vài dòng lãng đãng thế.
 
Upvote 0
Muốn biết tim tức về một sheet thì có thể tra cứu cấu hình hạ tầng của file (metastructure).
Có nhiều cách để tra cứu. Cách dễ nhất là lợi dụng các Objects của Windows.
File Excel có thể được thông qua bởi một trong hai Objects: Data Access Objects (DAO) và ActiveX Data Objects (ADO).
Chịu khó tra cứu bài "Đố Vui Về DAO và ADO" của Hai Lúa Miền Tây bên hộp CSDL.
 
Upvote 0
Làm cho Excel2003, các bạn test thử và mở rộng thêm:
Mã:
Function SheetExists(ByVal strExcelFullName As String, ByVal strSheetName As String) As Boolean
    SheetExists = False
    On Error GoTo ErrorHandler
    Dim objExcel As Object
    Set objExcel = CreateObject("ADODB.Recordset")
    Dim FileExists As Boolean
    FileExists = (GetAttr(strExcelFullName) And vbNormal) = vbNormal
    Dim sAppEName As String


    If FileExists Then
        sAppEName = "DRIVER=Microsoft Excel Driver (*.xls);" & "DBQ=" & strExcelFullName
        objExcel.Open "SELECT * FROM [" & strSheetName & "$]", sAppEName
        SheetExists = True
    End If
    Exit Function
ErrorHandler:
    If Not FileExists Then MsgBox "Error Description : " & Err.Description
End Function

Ví dụ:
Mã:
Sub test()
    MsgBox SheetExists(ThisWorkbook.Path & "\Test.xls", "Sheet2")
End Sub
 
Upvote 0
Cảm ơn các bác đã quan tâm.
1. Có thể em dùng từ chưa được chính xác. Yêu cầu chính xác là "...mà không nhìn thấy mở file"
2. Mô tả công việc cụ thể thêm: Tổng hợp từ nhiều file vào 1 file
- Từ file tổng hợp chọn thư mục chứa các file.
- Dùng công thức dạng link (=thumuc\[file]sheet!range)
- Bỏ công thức (value=value)
 
Upvote 0
Cảm ơn các bác đã quan tâm.
1. Có thể em dùng từ chưa được chính xác. Yêu cầu chính xác là "...mà không nhìn thấy mở file"
2. Mô tả công việc cụ thể thêm: Tổng hợp từ nhiều file vào 1 file
- Từ file tổng hợp chọn thư mục chứa các file.
- Dùng công thức dạng link (=thumuc\[file]sheet!range)
- Bỏ công thức (value=value)

Tức là lấy dữ liệu từ file đang đóng!
Ngay từ đầu mô tả vậy luôn đi, chứ cái gì mà kiểm tra sheet tồn tại. Bạn cứ hướng người ta đi theo con đường tầm bậy tầm bạ (mà bạn cứ cho là đúng)
Tóm lại: Yêu cầu cuối cùng của bạn là gì thì cứ mô tả như vậy. Đưa file lên sẽ có kết quả sớm (còn không thì cứ cù nhầy mãi thôi)
-------------------------------------
Làm cho Excel2003, các bạn test thử và mở rộng thêm:
Mã:
Function SheetExists(ByVal strExcelFullName As String, ByVal strSheetName As String) As Boolean
    SheetExists = False
    On Error GoTo ErrorHandler
    Dim objExcel As Object
    Set objExcel = CreateObject("ADODB.Recordset")
    Dim FileExists As Boolean
    FileExists = (GetAttr(strExcelFullName) And vbNormal) = vbNormal
    Dim sAppEName As String


    If FileExists Then
        sAppEName = "DRIVER=Microsoft Excel Driver (*.xls);" & "DBQ=" & strExcelFullName
        objExcel.Open "SELECT * FROM [" & strSheetName & "$]", sAppEName
        SheetExists = True
    End If
    Exit Function
ErrorHandler:
    If Not FileExists Then MsgBox "Error Description : " & Err.Description
End Function

Ví dụ:
Mã:
Sub test()
    MsgBox SheetExists(ThisWorkbook.Path & "\Test.xls", "Sheet2")
End Sub
Chú ý:
- Tên file, tên sheet là tiếng Việt có dấu
- Nếu người ta bỏ đối số TÊN FILE thì đồng nghĩa muốn nói đến file hiện hành
 
Upvote 0
Thì bạn làm 1 cái thử xem thế nào
(Tôi cũng đang làm 1 cái tương tự nhưng.. đang test...)

Em thử viết như vầy:
[GPECODE=vb]
Function SheetExist(SheetName As String, Optional FileName As String = "") As Boolean
Dim tmp As String
With New ADODB.Connection
.ConnectionString = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & IIf(FileName = "", ThisWorkbook.FullName, FileName) & _
";Extended Properties=""Excel 12.0;HDR=No;"";"
.Open
With .OpenSchema(adSchemaTables)
While Not .EOF
If .Fields("TABLE_TYPE") = "TABLE" Then
tmp = Replace(.Fields("TABLE_NAME"), "'", "")
If UCase(tmp) Like UCase(SheetName & "$") Then SheetExist = True
End If
.MoveNext
Wend
End With
End With
End Function
[/GPECODE]
 
Upvote 0
Em thử viết như vầy:
Mã:
Function SheetExist(SheetName As String, Optional FileName As String = "") As Boolean
Dim tmp As String
    [COLOR=#ff0000]With New ADODB.Connection[/COLOR]
        .ConnectionString = "[COLOR=#0000cd]Provider=Microsoft.ACE.OLEDB.12.0[/COLOR];" & _
                            "Data Source=" & IIf(FileName = "", ThisWorkbook.FullName, FileName) & _
                            ";Extended Properties=""Excel 12.0;HDR=No;"";"
        .Open
        [COLOR=#ff0000]With .OpenSchema(adSchemaTables)[/COLOR]
            While Not .EOF
                If .Fields("TABLE_TYPE") = "TABLE" Then
                    tmp = Replace(.Fields("TABLE_NAME"), "'", "")
                    If UCase(tmp) Like UCase(SheetName & "$") Then SheetExist = True
                End If
                .MoveNext
            Wend
        End With
    End With
End Function
Viết code kiểu đó ai mà test được đây
Viết dưới dạng CreateObject(...) đi
Nếu người ta dùng Excel 2003 thì chổ màu xanh xem như.. toi
-----------------
Thử dùng DAO xem sao
Mã:
Function SheetExists(ByVal SheetName As String, Optional ByVal FileName As String = "") As Boolean
  Dim dao  As Object, db As Object
  Dim i As Long, lVer As Long
  Dim tmp As String
  On Error Resume Next
  lVer = Val(Application.Version)
  If Len(FileName) = 0 Then FileName = ThisWorkbook.FullName
  Set dao = CreateObject("DAO.DBEngine." & IIf(lVer < 12, "36", "120"))
  Set db = dao.OpenDatabase(FileName, False, False, "Excel 8.0;")
  tmp = CStr(db.TableDefs(SheetName & "$").Name)
  If Len(tmp) = 0 Then tmp = CStr(db.TableDefs("'" & SheetName & "$'").Name)
  SheetExists = (Len(tmp) > 0)
  db.Close: Set dao = Nothing: Set db = Nothing
End Function
 
Lần chỉnh sửa cuối:
Upvote 0
@thanhlanh:

Code của bạn không sai nhưng có hai điểm mà người lập trình nên tránh:

1. Code không huỷ object sau khi sử dụng

2. Code dùng hiện tượng bắt lỗi để làm việc chính. Đối với lập trình, hiện tượng bắt lỗi là chuyện cực chẳng đã, chỉ dùng để bắt những trường hợp không biết trước. Nếu có cách nào khác, người ta luôn luôn chọn cách khác.

Đọc Schema qua ADO hay DAO như hai bạn trên là cách đơn giản hơn.

Tôi xin hầu thêm cách đọc Catalog qua ADOX

Mã:
Function SheetExists(ByVal fileName As String, ByVal sheetName As String)
Dim cn As ADODB.Connection
Dim cat As ADOX.Catalog
Dim t As ADOX.Table

SheetExists = False
Set cn = New ADODB.Connection
cn.Open "Provider=MSDASQL.1;Data Source=Excel Files;" _
& "Initial Catalog=" & fileName
Set cat = New ADOX.Catalog
Set cat.ActiveConnection = cn
For Each t In cat.Tables
    If t.Name = sheetName & "$" Then
        SheetExists = True
        Exit For
    End If
Next t
Set cat = Nothing
cn.Close
Set cn = Nothing
End Function
 
Upvote 0
@thanhlanh:

Code của bạn không sai nhưng có hai điểm mà người lập trình nên tránh:

1. Code không huỷ object sau khi sử dụng

2. Code dùng hiện tượng bắt lỗi để làm việc chính. Đối với lập trình, hiện tượng bắt lỗi là chuyện cực chẳng đã, chỉ dùng để bắt những trường hợp không biết trước. Nếu có cách nào khác, người ta luôn luôn chọn cách khác.

Đọc Schema qua ADO hay DAO như hai bạn trên là cách đơn giản hơn.

Tôi xin hầu thêm cách đọc Catalog qua ADOX

Mã:
Function SheetExists(ByVal fileName As String, ByVal sheetName As String)
Dim cn As ADODB.Connection
Dim cat As ADOX.Catalog
Dim t As ADOX.Table

SheetExists = False
Set cn = New ADODB.Connection
cn.Open "Provider=MSDASQL.1;Data Source=Excel Files;" _
& "Initial Catalog=" & fileName
Set cat = New ADOX.Catalog
Set cat.ActiveConnection = cn
For Each t In cat.Tables
    If [COLOR=#ff0000]t.Name = sheetName & "$"[/COLOR] Then
        SheetExists = True
        Exit For
    End If
Next t
Set cat = Nothing
cn.Close
Set cn = Nothing
End Function
Có mấy vấn đề
- Chổ màu đỏ phải lòng UCase hoặc LCase vào mới xong!
- Nếu tên sheet có chứa khoảng trắng thì t.Name sẽ có 2 dấu ' ở 2 đầu
- Code trên không dùng được nếu tên sheet là chuổi tiếng Việt có dấu
Thuật toán là vậy nhưng phải sửa tiếp thì code trên mới hoàn chỉnh
 
Lần chỉnh sửa cuối:
Upvote 0
Cảm ơn bạn đã dẫn. Tôi cứ ngỡ code trên dùng ODBC nên thoát khỏi cảnh phải lồng giữa 2 dấu nháy. Không chịu thử cho kỹ.
Hoá ra đọc theo tầng trên (metadata) còn tệ hơn đọc theo kiểu dữ liệu thô.
 
Upvote 0
Cảm ơn bạn đã dẫn. Tôi cứ ngỡ code trên dùng ODBC nên thoát khỏi cảnh phải lồng giữa 2 dấu nháy. Không chịu thử cho kỹ.
Hoá ra đọc theo tầng trên (metadata) còn tệ hơn đọc theo kiểu dữ liệu thô.

Thật ra thì tôi có biết cái quỷ gì về ADO hay DAO đâu. Chẳng qua cách đây vài năm có từng làm 1 code với ứng dụng tương tự nên gần như đã "thuộc lòng" những sự cố cần phải tránh
Nói về kiến thức ADO, DAO thì: Nếu bạn biết được 10 phần, tôi chỉ biết được có 0.000...01
Ẹc... Ẹc...
 
Upvote 0
@thanhlanh:

Code của bạn không sai nhưng có hai điểm mà người lập trình nên tránh:

1. Code không huỷ object sau khi sử dụng

2. Code dùng hiện tượng bắt lỗi để làm việc chính. Đối với lập trình, hiện tượng bắt lỗi là chuyện cực chẳng đã, chỉ dùng để bắt những trường hợp không biết trước. Nếu có cách nào khác, người ta luôn luôn chọn cách khác.

Đọc Schema qua ADO hay DAO như hai bạn trên là cách đơn giản hơn.

Tôi xin hầu thêm cách đọc Catalog qua ADOX

Mã:
Function SheetExists(ByVal fileName As String, ByVal sheetName As String)
Dim cn As ADODB.Connection
Dim cat As ADOX.Catalog
Dim t As ADOX.Table

SheetExists = False
Set cn = New ADODB.Connection
cn.Open "Provider=MSDASQL.1;Data Source=Excel Files;" _
& "Initial Catalog=" & fileName
Set cat = New ADOX.Catalog
Set cat.ActiveConnection = cn
For Each t In cat.Tables
    If t.Name = sheetName & "$" Then
        SheetExists = True
        Exit For
    End If
Next t
Set cat = Nothing
cn.Close
Set cn = Nothing
End Function

Cảm ơn bạn đã góp ý, mình đúng là cực chẳng đã mới dùng bẫy lỗi để làm việc, bởi vì còn tệ hơn ndu, mình đâu biết gì về ADO, DAO hay ODA đâu, nhìn giống Tôn Hành Giả, Giả Hành Tôn quá. Bây giờ bạn còn đưa ADOX vào nữa thì có khi mình sẽ rối loạn tiền đình đó.
Tuy không đúng yêu cầu (sau) của tác giả topic nhưng tạo được hàm trong VBA để kiểm tra sự tồn tại của Sheets khi không mở file cũng hay và đáng quan tâm chớ nhỉ?
 
Lần chỉnh sửa cuối:
Upvote 0
Vấn đề này dùng hàm Macro4 cũng tốt mà gọn. Mình ví dụ như sau

Mã:
Option Explicit
Function SheetExist(ByVal mPath As String, fName As String, ShName As String) As Boolean
Dim Tm, myVar
On Error Resume Next
Tm = "'" & mPath & "[" & fName & "]" & ShName & "'!" & _
Range("A1").Address(, , xlR1C1)
myVar = ExecuteExcel4Macro(Tm)
SheetExist = Not IsEmpty(myVar)
End Function
'--------------------------------------------------------------------------------
Sub Test()
Dim myPath As String, myFile As String, mySheet As String
myPath = Thisworkbook.Path & "\"
myFile = "Mau danh cho CC BCTC48.xls"
mySheet = "B-01"
MsgBox IIf(SheetExist(myPath, myFile, mySheet), _
"Exist sheet: " & mySheet, "The sheet: " & mySheet & " not foumd")
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
mình đâu biết gì về ADO, DAO hay ODA đâu
Mặc dù em không có vay hoặc cho vay nhưng em biết cái ODA là gì đó anh
Ẹc... Ẹc...
-------------------------
Vấn đề này dùng hàm Macro4 cũng tốt mà gọn. Mình ví dụ như sau

Mã:
Option Explicit
Function SheetExist(ByVal mPath As String, fName As String, ShName As String) 'As Boolean
Dim Tm
On Error Resume Next
Tm = "'" & mPath & "[" & fName & "]" & ShName & "'!" & _
Range("A1").Address(, , xlR1C1)
SheetExist = ExecuteExcel4Macro(Tm)
End Function
'-----------------------------------------------------------------------------------
Sub Test()
Dim myPath As String, myFile As String, mySheet As String
myPath = Thisworkbook.Path & "\"
myFile = "Mau danh cho CC BCTC48.xls"
mySheet = "B-01"
MsgBox IIf(IsEmpty(SheetExist(myPath, myFile, mySheet)), _
"The sheet: " & mySheet & " not foumd", "Exist sheet: " & mySheet)
End Sub
Code này chỉ chạy trên VBA như 1 sub thì được... Anh thử gõ trực tiếp hàm xuống sheet xem nó ra kết quả gì?
 
Lần chỉnh sửa cuối:
Upvote 0
Mặc dù em không có vay hoặc cho vay nhưng em biết cái ODA là gì đó anh
Ẹc... Ẹc...
-------------------------

Code này chỉ chạy trên VBA như 1 sub thì được... Anh thử gõ trực tiếp hàm xuống sheet xem nó ra kết quả gì?

Mình viết lại rồi, chỉ thay đổi cách gán giá trị cho Hàm thôi
 
Upvote 0
Mình viết lại rồi, chỉ thay đổi cách gán giá trị cho Hàm thôi
Test trong môi trường VBA thì được:
Mã:
Sub Test()
 Dim mPath As String, fName As String, ShName As String
 mPath = [A5]
 fName = [A6]
 ShName = [B1]
 MsgBox SheetExist(mPath, fName, ShName)
End Sub

Nhưng nếu anh gõ hàm trực tiếp xuống sheet, ví dụ: =SheetExist(A5, A6, B1) với A5 là thư mục, A5 là tên file và B1 là tên sheet ---> Kết quả toàn = FALSE thôi anh à
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng vậy, mình toàn test luôn trên VBA nên không lường ra trường hợp này.
 
Upvote 0
@thanhlanh:

Code của bạn không sai nhưng có hai điểm mà người lập trình nên tránh:

1. Code không huỷ object sau khi sử dụng

2. Code dùng hiện tượng bắt lỗi để làm việc chính. Đối với lập trình, hiện tượng bắt lỗi là chuyện cực chẳng đã, chỉ dùng để bắt những trường hợp không biết trước. Nếu có cách nào khác, người ta luôn luôn chọn cách khác.

Đọc Schema qua ADO hay DAO như hai bạn trên là cách đơn giản hơn.

Tôi xin hầu thêm cách đọc Catalog qua ADOX

Mã:
Function SheetExists(ByVal fileName As String, ByVal sheetName As String)
Dim cn As ADODB.Connection
Dim cat As ADOX.Catalog
Dim t As ADOX.Table

SheetExists = False
Set cn = New ADODB.Connection
cn.Open "Provider=MSDASQL.1;Data Source=Excel Files;" _
& "Initial Catalog=" & fileName
Set cat = New ADOX.Catalog
Set cat.ActiveConnection = cn
For Each t In cat.Tables
    If t.Name = sheetName & "$" Then
        SheetExists = True
        Exit For
    End If
Next t
Set cat = Nothing
cn.Close
Set cn = Nothing
End Function
Nếu kiểm tra sự tồn tại của 1 file nào đó thì chỉ cần kiểm tra trong file đó có 1 sheet là xem như file đó đã tồn tại, cần gì phải dùng vòng lặp duyệt qua rồi tìm thấy rồi dừng vòng lặp?
 
Upvote 0
Test trong môi trường VBA thì được:
Mã:
Sub Test()
 Dim mPath As String, fName As String, ShName As String
 mPath = [A5]
 fName = [A6]
 ShName = [B1]
 MsgBox SheetExist(mPath, fName, ShName)
End Sub

Nhưng nếu anh gõ hàm trực tiếp xuống sheet, ví dụ: =SheetExist(A5, A6, B1) với A5 là thư mục, A5 là tên file và B1 là tên sheet ---> Kết quả toàn = FALSE thôi anh à
Dùng trong VBA được là tốt rồi, miễn sao trả về kết quả chính xác với mọi phiên bản Excel và với mọi tên Sheet, tên file.
Mình "tín nhiệm cao" ndu test cho code của anh sealand.
 
Upvote 0
Nếu kiểm tra sự tồn tại của 1 file nào đó thì chỉ cần kiểm tra trong file đó có 1 sheet là xem như file đó đã tồn tại, cần gì phải dùng vòng lặp duyệt qua rồi tìm thấy rồi dừng vòng lặp?

Bạn có thấy tôi dùng từ "đơn giản" không?
Định nghĩa đơn giản là dễ hiểu dễ dùng dễ sửa. Theo quan điểm của lập trình thì quan trọng nhất là "nếu có chỗ sai thì dễ nhìn ra"
 
Upvote 0
Bạn có thấy tôi dùng từ "đơn giản" không?
Định nghĩa đơn giản là dễ hiểu dễ dùng dễ sửa. Theo quan điểm của lập trình thì quan trọng nhất là "nếu có chỗ sai thì dễ nhìn ra"
Tại sao bạn ghi là đơn giản mà trong khi đó phải dùng thêm vòng lặp? Có phải bị thừa?
 
Upvote 0
Tại sao bạn ghi là đơn giản mà trong khi đó phải dùng thêm vòng lặp? Có phải bị thừa?

Ah... đơn giản nó khác với code ngắn gọn nha
Lúc đầu tôi viết code kiểm tra sự tồn tại của sheet, tôi dùng DAO cũng thông qua vòng lập. Sau đó mới sửa lại thành không vòng lập và tôi nhận xét thấy:
- Có vòng lập thì dễ nhìn hơn (dễ hiểu tức là đơn giản)
- Không vòng lập: Code ngắn hơn (nhưng hơi khó hiểu hơn)
Mặc khác: Chưa chắc khi ta không dùng vòng lập thì tốc độ sẽ cao hơn đâu
 
Lần chỉnh sửa cuối:
Upvote 0
Ah... đơn giản nó khác với code ngắn gọn nha
Lúc đầu tôi viết code kiểm tra sự tồn tại của sheet, tôi dùng DAO cũng thông quan vòng lập. Sau đó mới sửa lại thành không vòng lập và tôi nhận xét thấy:
- Có vòng lập thì dễ nhìn hơn (dễ hiểu tức là đơn giản)
- Không vòng lập: Code ngắn hơn (nhưng hơi khó hiểu hơn)
Mặc khác: Chưa chắc khi ta không dùng vòng lập thì tốc độ sẽ cao hơn đâu
Vòng lặp để làm gì khi ta xác định chỉ có 1
 
Upvote 0
Khi dùng các Objects của Windows để lấy dữ liệu từ file thì tốc độ kết nối mới quan trọng. Chỉ khi nào recordset có hằng ngàn dòng thì mới tính đến tốc độ đường truyền. Sau đường truyền mới tính đến chuyện tốc độ code. Khi ấy nếu muốn tìm sự hiện hữu của một cái gì đó thì ta dùng câu truy vấn Select+where và xét sô recordcount của recordset.

Trong bài này thời gian kết nối bằng hàng triệu lần thời gian tính.
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom