2015-09-28 353 views
1

我想读在VBA大文件,并且看到这个代码在线:如何读取一个文本文件中的倒数第二行

Dim MyChar As String, Pointer As Long, LastLine As String 
Open "MyTextFile.Txt" For Binary As #1 
Pointer = LOF(1) - 2 
MyChar = Chr$(32) 
Do 
    Get #1, Pointer, MyChar 
    If MyChar = vbCr Or MyChar = vbLf Then 
     Exit Do 
    Else: Pointer = Pointer - 1 
     LastLine = MyChar & LastLine 
    End If 
Loop 
MsgBox "Last Line is " & LastLine 

如何改变这种代码来获取倒数第二行?需要一些帮助。

想到这里:

Set objFSO = CreateObject("Scripting.FileSystemObject") 
Set objTextFile = objFSO.OpenTextFile _ 
    "MyTextFile.Txt", 1) 
objTextFile.ReadAll 
MsgBox objTextFile.Line 

但我不能站上罚球线,2。

回答

0

你提供工作的代码如下:

  1. 它设置一个指向文件的最后一个字符
  2. 它然后读取该文件向后,直到它找到一个换行符
  3. 它返回所有它读作为最后一行。

要修改此为您的需求,我添加了一个Boolean secondRun,这再次让代码运行步骤2中,从而记录第二个最后一行:

Dim MyChar As String, Pointer As Long, LastLine As String 
Open "MyTextFile.Txt" For Binary As #1 
Pointer = LOF(1) - 2 
MyChar = Chr$(32) 
Dim secondRun As Boolean 
Do 
    ' Read character at position "Pointer" into variable "MyChar" 
    Get #1, Pointer, MyChar 
    If MyChar = vbCr Or MyChar = vbLf Then ' Linebreak = line read completely 
     If Not secondRun Then 
      ' Run again if we've read only one line so far 
      secondRun = True 
      LastLine = "" 
      Pointer = Pointer - 2 
     Else 
      Exit Do 
     End If 
    Else: Pointer = Pointer - 1 
     ' Add character to result String 
     LastLine = MyChar & LastLine 
    End If 
Loop 
MsgBox " 2nd last line is " & LastLine 
1

取决于你的方法。但是,如果文件真的很大,那么你可能不希望Excel加载整个文件。所以,你可能会打开文件并逐行阅读,而不知道文件有多大以及它有多少行。在这种情况下,最简单的做法是一次只将两行存储在两个单独的字符串变量中。只要你点击最后一行,你可以退出你的循环 - 如上面的代码所示 - 并且不仅输出最后一行(如代码中已经完成的那样),而且还输出该文件中倒数第二行的内容。

Public Sub GetSecondLastRow() 
Dim strSecondLastLine As String 
Dim strFileToImport As String 
Dim strLastLine As String 
Dim intPointer As Integer 
Dim lngCounter As Long 

strFileToImport = ThisWorkbook.Path & IIf(InStr(1, ThisWorkbook.Path, "\") > 0, "\", "/") & "MyTextFile.txt" 

intPointer = FreeFile() 
Open strFileToImport For Input Access Read Lock Read As #intPointer 

lngCounter = 0 
Do Until EOF(lngCounter) 
    strSecondLastLine = strLastLine 
    Line Input #intPointer, strLastLine 
    lngCounter = lngCounter + 1 
Loop 

Close intPointer 

Debug.Print "Content of the second last row:" 
Debug.Print "---------------------------------------" 
Debug.Print strSecondLastLine 
Debug.Print "---------------------------------------" 
Debug.Print "Content of the last row:" 
Debug.Print "---------------------------------------" 
Debug.Print strLastLine 

End Sub 

另一种方法是首先查询文件的行数,然后使用ADO获取该文件的第二个最后记录。但我怀疑这会更快。 ADO的问题在于你得到一个包含整个文本文件的巨大的recordset。这是由于您在条款SELECT * from MyTextFile.txt中没有任何限制。所以,整个文本文件在之前进入内存,你可以用它做任何事情。然后 - 当然 - 您可以检查RecordCount,并再次通过光标快进的所有记录,直到您击中最后一行。不幸的是,ADO不支持

row_number()over(order by @@ ROWCOUNT)。

否则,您可以首先获得行计数select count(1) from MyTextFile.txt,然后只有适用的行。因此,无论如何,我几乎可以肯定(没有经过测试),ADO的表现将低于标准杆,而第一种解决方案是如果文本文件与您所说的一样大的话,要走的路。如果你仍然喜欢ADO,那么这是代码(基于以下SO问题/回答:Copying text from .txt file in Excel using ADO ignores first row)。

Sub ImportTextFile() 

'Imports text file into Excel workbook using ADO. 
'If the number of records exceeds 65536 then it splits it over more than one sheet. 
Dim strFilePath As String, strFilename As String, strFullPath As String 
Dim lngCounter As Long 
Dim oConn As ADODB.Connection 
Dim oRS As ADODB.Recordset 
Dim oFSObj As Object 

'Get a text file name 
strFullPath = Application.GetOpenFilename("Text Files (*.txt),*.txt", , "Please select text file...") 

If strFullPath = "False" Then Exit Sub 'User pressed Cancel on the open file dialog 

'This gives us a full path name e.g. C:\temp\folder\file.txt 
'We need to split this into path and file name 
Set oFSObj = CreateObject("SCRIPTING.FILESYSTEMOBJECT") 

strFilePath = oFSObj.GetFile(strFullPath).ParentFolder.Path 
strFilename = oFSObj.GetFile(strFullPath).Name 

'Open an ADO connection to the folder specified 
Set oConn = New ADODB.Connection 
oConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _ 
       "Data Source=" & strFilePath & ";" & _ 
       "Extended Properties=""text;HDR=No;FMT=Delimited""" 

Set oRS = New ADODB.Recordset 
'Now actually open the text file and import into Excel 
oRS.Open "SELECT count(1) FROM [" & strFilename & "]", oConn, 3, 1, 1 

Range("A1").CopyFromRecordset oRS 

Set oRS = New ADODB.Recordset 
'Now actually open the text file and import into Excel 
oRS.Open "SELECT * FROM [" & strFilename & "]", oConn, 3, 1, 1 

While Not oRS.EOF And Not oRS.BOF 
    If oRS.AbsolutePosition = Range("A1").Value2 Then 
     Range("A2").Value = oRS.Fields(0).Value 
    End If 
    oRS.MoveNext 
Wend 

oRS.Close 
oConn.Close 

End Sub 
+0

我更喜欢第二种方法。如何使用ADO获取文件?可以进一步解释? – lakesh

+0

刚刚更新了我的答案,解释为什么ADO不值得深入研究。但是,如果你仍然想要沿着这条路线,那么简单地从以下任何一个复制:http://stackoverflow.com/questions/16898046/copying-text-from-txt-file-in-excel-using-ado-ignores-第一行http://stackoverflow.com/questions/22947425/using-ado-to-query-text-files-terrible-performance步骤是一样的:(1)'从SELECT * MyTextFile.txt'然后保存总是两个字符串并循环到'recordset'的末尾。一旦你在最后你可以输出最后两行。 – Ralph

0

你可以试试这个:

Public Function GetSecondLastLine(sFileName As String, Optional sLineDelimiter As String = vbCrLf) As String 

    Dim sContent As String 
    Dim aLines() As String 

    sContent = TextFromFile(sFileName) 

    aLines = Split(sContent, sLineDelimiter) 

    GetSecondLastLine = aLines(UBound(aLines) - 1) 

End Function 

Public Function TextFromFile(sFileName As String) As String 

    Dim lFile As Long 

    lFile = FreeFile 
    Open sFileName For Input As #lFile 
    TextFromFile = Input$(LOF(lFile), lFile) 
    Close #lFile 

End Function 

如果需要,您可以更改行分隔符(例如vbLF的VBCR)

+0

该文件非常大。所以它给我的内存不足 – lakesh

+0

在这种情况下,你可以通过提取最后一个例如缓冲区来创建一个缓冲区。 10000个字符作为字符串并将其用作GetSecondLastLine中的sContent –