Concatenating lines from text file - Excel VBA - vba

I have a text file in a format like so,
Text:
-- Begin
Line1
Line2
Line3
^
-- Begin
Line1
Line2
Line3
Line4
^
.
.
.
.
I basically want to put Line1 to Line(whatever) between the lines --Begin to ^
in an array so each element in the array is bunch of lines Example of array
Array = [("Line1" & vbNewLine & "Line 2") , ("Line1" & vbNewLine & "Line 2" & vbNewLine & "Line 3") ... ]
But basically want to store each element in the array in a cell. (Might not even need to use an array) ...
Not sure if this is even possible in excel VBA, but this is what I've tried so far
Dim FileNum As Integer
Dim DataLine As String
Dim Lines As Variant
Dim j As Integer
FileNum = FreeFile()
Open "C:..." For Input As #FileNum
While Not EOF(FileNum)
Line Input #FileNum, DataLine
If InStr(DataLine, "-- Begin") > 0 Then
nextLinecounter = 1
ElseIf InStr(DataLine, "^") > 0 Then
nextLinecounter = 0
j = j + 1
ElseIf nextLinecounter = 1 Then
Lines(j) = DataLine + .. Somehow concatenate next lines into array
End If
Wend
I'm stuck how to skip next line and append it to the current entry, any way of doing this thanks.

So I would do it a bit differently. Using a more up to date approach for file reading.
See more details on how to read *.txt files in VBA - here
Note: you need to add references to Microsoft Scripting Runtime via VBE -> Tools -> References
Option Explicit
Sub ReadTxtFile()
Dim oFSO As New FileSystemObject
Dim oFS As TextStream
Dim filePath As String
filePath = "C:\Users\" & Environ$("username") & "\Desktop\foo.txt"
If Not fileExist(filePath) Then GoTo FileDoesntExist
On Error GoTo Err
ReDim arr(0) As String
Dim s As String
Set oFS = oFSO.OpenTextFile(filePath)
Do While Not oFS.AtEndOfStream
Dim line As String
line = oFS.ReadLine
If InStr(line, "-- Begin") = 0 And InStr(line, "^") = 0 Then
s = s & line
End If
If InStr(line, "^") > 0 Then
arr(UBound(arr)) = s
ReDim Preserve arr(UBound(arr) + 1)
s = vbNullString
End If
Loop
ReDim Preserve arr(UBound(arr) - 1)
oFS.Close
Dim k As Long
For k = LBound(arr) To UBound(arr)
Debug.Print k, arr(k)
Next k
Exit Sub
FileDoesntExist:
MsgBox "File Doesn't Exist", vbCritical, "File not found!"
Exit Sub
Err:
MsgBox "Error while reading the file.", vbCritical, vbNullString
oFS.Close
Exit Sub
End Sub
Function fileExist(path As String) As Boolean
fileExist = IIf(Dir(path) <> vbNullString, True, False)
End Function
the foo.txt looks like this
-- Begin
Line1
Line2
Line3
^
-- Begin
Line1
Line2
Line3
Line4
^
your array looks like this

Related

Parse and format text file

I have a text file that is not in a format that I can use for printing labels. The current format is like this:
DY234-02 0.5 0.5 Qty 6
U21 U12 U14 U28
TR459-09 0.5 0.5 Qty 9
U11 U78 U7 U8 U30 U24
I need the file to end up like this:
DY234-02 0.5 0.5 Qty 6 U21 U12 U14 U28
TR459-09 0.5 0.5 Qty 9 U11 U78 U7 U8 U30 U24
The files contain about 100 lines of this format I have used vbscript to try to get what I need but the format is not much different. If someone could get me pointed in the right direction that would be great. I am open to all other methods for accomplishing this. Thanks
This is my code in vbscript, but is not doing the job correctly:
Const ForReading = 1
Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\Scripts\parse.txt", ForReading)
Do Until objFile.AtEndOfStream
strLine1 = objFile.ReadLine
strLine2 = ""
If Not objFile.AtEndOfStream Then
strLine2 = objFile.ReadLine
End If
strNewLine = strLine1 & strLine2
strNewContents = strNewContents & strNewLine & vbCrLf
Loop
objFile.Close
Set objFile = objFSO.OpenTextFile("C:\Scripts\B3.txt", ForWriting, True)
objFile.Write strNewContents
objFile.Close
If the format is repeated like this, you can read in the text file line by line, and check if there is data on each line. If so join the data to an output string, otherwise add a carriage return to the output string, before finally outputting it to a new text file. Something like this perhaps:
Dim strInFile As String
Dim strOutFile As String
Dim intInFile As Integer
Dim intOutFile As Integer
Dim strInput As String
Dim strOutput As String
strInFile = "J:\downloads\data-in.txt"
strOutFile = "J:\downloads\data-out.txt"
intInFile = FreeFile
Open strInFile For Input As intInFile
intOutFile = FreeFile
Open strOutFile For Output As intOutFile
Do
Line Input #intInFile, strInput
If Len(Trim(strInput)) > 0 Then
strOutput = strOutput & " " & strInput
Else
strOutput = strOutput & vbCrLf
End If
Loop Until EOF(intInFile)
Print #intOutFile, strOutput
Reset
Regards,
Try next code, please. It is fast due to the fact it reads all the text value at once and drop the result, also at once. Everything is happening in memory.
Sub testSplitTextFile()
Dim objFSO As Object, objTF As Object, strIn As String, fullFilename As String, retFile As String
Dim arrIn As Variant, strRet As String, i As Long
'use here your path
fullFilename = "C:\Teste VBA Excel\Teste StackOverflow\TestSplit.txt"
retFile = "C:\Teste VBA Excel\Teste StackOverflow\RetFile.txt"'your path
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTF = objFSO.OpenTextFile(fullFilename, 1)
strIn = objTF.ReadAll 'it reads all the txt file string
objTF.Close
arrIn = Split(strIn, vbCrLf) 'it splits the above string on lines
'Then, it builds a string based on your conditions:
For i = 0 To UBound(arrIn) - 1
If arrIn(i) <> "" And arrIn(i + 1) <> "" Then
strRet = strRet & arrIn(i) & " " & arrIn(i + 1) & vbCrLf
End If
Next i
strRet = left(strRet, Len(strRet) - 1)' it eliminates the last vbCrLf character
FreeFile 1
Open retFile For Output As #1
Print #1, strRet 'it drops, at once the created string
Close #1
End Sub

How to modify file line by line

I have a txt file in this syntax:
'foo','bar'
'foo','foo bar'
'foo','foo
bar bar'
'bar', 'foo'
I want to find each line that doens't start with ' and correct them. I want to end with:
'foo','bar'
'foo','foo bar'
'foo','foo bar bar'
'bar', 'foo'
The new line must be removed and added to the end of the previous line with a leading space.
My code cicles through a file line by line and checks, if the first character is unequal to 'already.
I thought about adding every line to an array and do the correction within that array.
I'd use
Open myFile For Output As #1
Write #1, lineOfArray
Close #1
to update the file.
My current code:
Sub Update_File(fileToUpdate As String, fileSys As Variant)
Set File = fileSys.OpenTextFile(fileToUpdate)
Do Until File.AtEndOfStream
currentLine = File.ReadLine
If Left(currentFile, 1) <> "'" Then
'Magic here
End If
Loop
File.Close
End Sub
I am struggeling with what is best practice and what is lean and quick code because the script should run over many 1000 files at the end.
I don't know if it is possible to store the current and the next line and if next line doesn't start with ' then currentLine = currentLine & " " & nextLine and somehow update the the file, decrease the loop value by one and go ahead.
Based on TinMan's wonderful code, try this vserion
Sub Test_UpdateFile()
UpdateFile ThisWorkbook.Path & "\Sample.txt", CreateObject("Scripting.FileSystemObject")
End Sub
Sub UpdateFile(ByVal filePath As String, ByRef fso As Object)
Const forReading = 1
Dim lines() As String
Dim dirty As Boolean
Dim r As Long
Dim x As Long
Dim k As Long
With fso.OpenTextFile(filePath, forReading)
lines = Split(.ReadAll, vbNewLine)
.Close
End With
For r = 1 To UBound(lines)
If Left(lines(r), 1) <> "'" Then
lines(r - 1) = lines(r - 1) & " " & lines(r)
lines(r) = Empty
dirty = True
x = x + 1
End If
Next r
If dirty Then
ReDim nLines(0 To UBound(lines) - x + 1)
For r = 0 To UBound(lines)
If lines(r) <> Empty Then
nLines(k) = lines(r)
k = k + 1
End If
Next r
With fso.CreateTextFile(filePath, True)
.Write Join(nLines, vbNewLine)
.Close
End With
End If
End Sub
Here is how I would do it.
Sub Update_File(ByVal FilePath As String, ByRef fso As Object)
Const ForReading = 1
Dim lines() As String
Dim r As Long
Dim Dirty As Boolean
With fso.OpenTextFile(FilePath, ForReading)
lines = Split(.ReadAll, vbNewLine)
.Close
End With
For r = 0 To UBound(lines)
If Left(lines(r), 1) <> "'" Then
lines(r) = "'" & lines(r)
Dirty = True
End If
Next
If Dirty Then
With fso.CreateTextFile(FilePath, True)
.Write Join(lines, vbNewLine)
.Close
End With
End If
End Sub
What about this:
currentline = File.ReadLine
NextLine = vbNullString
Do
If Not File.AtEndOfStream Then
NextLine = File.ReadLine
Do While Left$(NextLine, 1) <> "'"
If Len(currentline) > 0 Then currentline = Trim(currentline) & " "
currentline = currentline & Trim(NextLine)
NextLine = File.ReadLine
Loop
End If
Write #1, currentline
currentline = NextLine
Loop Until File.AtEndOfStream

Getting an Extra Empty line when exporting Excel Range to .txt file

I am trying to copy an Excel range to a .txt file.
The export is successful, with one exception, It adds one "extra" empty line at the end.
I've read and tests many of the solution on SO (and other sites), but still without any success.
My Code (relevant part)
' === Export to the .txt file ===
Dim TxtFileName As String, lineText As String
TxtFileName = ThisWorkbook.Path & "\Inv_" & Format(Date, "yyyymmdd") & ".txt"
Open TxtFileName For Output As #1
With StockSht
For i = 1 To LastRow
For j = 1 To 3
If j = 3 Then
lineText = lineText & .Cells(i, j).Value2
Else ' j = 1 or 2
lineText = lineText & .Cells(i, j).Value2 & vbTab
End If
Next j
Print #1, lineText
lineText = ""
Next i
End With
Close #1
My StockSht (worksheet object) and LastRow are defined correctly, and getting their values.
Screen-shot of the end of the exported .txt file
You can use a semi-colon in the Print statement to control the insertion point (i.e. prevent the line-feed on the last line).
The relevant bit on the MSDN page:
Use a semicolon to position the insertion point immediately after the last character displayed.
I tested this code:
Sub PrintTest()
Dim lng As Long
Open "C:\foo3.txt" For Output As #1
For lng = 1 To 10
If lng < 10 Then
Print #1, "foo" & lng
Else
Print #1, "foo" & lng; '<-- semi-colon prevents the newline
End If
Next lng
Close #1
End Sub
So I would update your code like below (not tested):
' === Export to the .txt file ===
Dim TxtFileName As String, lineText As String
TxtFileName = ThisWorkbook.Path & "\Inv_" & Format(Date, "yyyymmdd") & ".txt"
Open TxtFileName For Output As #1
With StockSht
For i = 1 To LastRow
For j = 1 To 3
If j = 3 Then
lineText = lineText & .Cells(i, j).Value2
Else ' j = 1 or 2
lineText = lineText & .Cells(i, j).Value2 & vbTab
End If
Next j
'--- new bit: check for i against LastRow and add the semicolon on last row
If i <> LastRow Then
Print #1, lineText
Else
Print #1, lineText; '<-- semi colon keeps insertion point at end of line
End If
lineText = ""
Next i
End With
Close #1
Try using a ; on the last print line.
' === Export to the .txt file ===
Dim TxtFileName As String, lineText As String
TxtFileName = ThisWorkbook.Path & "\Inv_" & Format(Date, "yyyymmdd") & ".txt"
Open TxtFileName For Output As #1
With StockSht
For i = 1 To LastRow
For j = 1 To 3
If j = 3 Then
lineText = lineText & .Cells(i, j).Value2
Else ' j = 1 or 2
lineText = lineText & .Cells(i, j).Value2 & vbTab
End If
Next j
If i = LastRow Then
Print #1, lineText;
Else
Print #1, lineText
End if
lineText = ""
Next i
End With
Close #1

Reading in data from text file into a VBA array

I have the following VBA code:
Sub read_in_data_from_txt_file()
Dim dataArray() As String
Dim i As Integer
Const strFileName As String = "Z:\sample_text.txt"
Open strFileName For Input As #1
' -------- read from txt file to dataArrayay -------- '
i = 0
Do Until EOF(1)
ReDim Preserve dataArray(i)
Line Input #1, dataArray(i)
i = i + 1
Loop
Close #1
Debug.Print UBound(dataArray())
End Sub
I'm trying to read in text line by line (assume 'sample.txt' is a regular ascii file) from a file and assign this data to consecutive elements in an array.
When I run this, I get all my data in the first value of the array.
For example, if 'sample.txt' is:
foo
bar
...
dog
cat
I want each one of these words in a consecutive array element.
What you have is fine; if everything ends up in dataArray(0) then the lines in the file are not using a CrLf delimiter so line input is grabbing everything.
Instead;
open strFileName for Input as #1
dataArray = split(input$(LOF(1), #1), vbLf)
close #1
Assuming the delimiter is VbLf (what it would be coming from a *nix system)
Here is a clean code on how to use for each loop in VBA
Function TxtParse(ByVal FileName As String) As String
Dim fs, ts As Object
Dim strdic() As String
Dim oitem As Variant
Set fs = CreateObject("Scripting.FileSystemObject")
Set ts = fs.OpenTextFile(FileName, 1, False, -2)
strdic = Split(ts.ReadAll, vbLf)
For Each oitem In strdic
If InStr(oitem, "YourString") <> 0 Then
Else
If InStr(1, oitem, vbTab) <> 0 Then
Debug.Print "Line number is : "; "'" & Replace(oitem, vbTab, "','") & "'"
Else
Debug.Print "Line number is : "; "'" & Replace(oitem, ",", "','") & "'"
End If
End If
Next
End Function

How to execute this Excel to XML function in a sub?

Can someone assist with how I can use this function below that converts my data in an excel file to an XML file in a sub? When I go to create a macro it by default has it for sub but I need to have it as a function. I need to be able to use this as maybe a custom button on the toolbar possibly or how can I use it for any spreadsheet I need to convert it from Excel to an XML file?
Public Function ExportToXML(FullPath As String, RowName _
As String) As Boolean
On Error GoTo ErrorHandler
Dim colIndex As Integer
Dim rwIndex As Integer
Dim asCols() As String
Dim oWorkSheet As Worksheet
Dim sName As String
Dim lCols As Long, lRows As Long
Dim iFileNum As Integer
Set oWorkSheet = ThisWorkbook.Worksheets(1)
sName = oWorkSheet.Name
lCols = oWorkSheet.Columns.Count
lRows = oWorkSheet.Rows.Count
ReDim asCols(lCols) As String
iFileNum = FreeFile
Open FullPath For Output As #iFileNum
For i = 0 To lCols - 1
'Assumes no blank column names
If Trim(Cells(1, i + 1).Value) = "" Then Exit For
asCols(i) = Cells(1, i + 1).Value
Next i
If i = 0 Then GoTo ErrorHandler
lCols = i
Print #iFileNum, "<?xml version=""1.0""?>"
Print #iFileNum, "<" & sName & ">"
For i = 2 To lRows
If Trim(Cells(i, 1).Value) = "" Then Exit For
Print #iFileNum, "<" & RowName & ">"
For j = 1 To lCols
If Trim(Cells(i, j).Value) <> "" Then
Print #iFileNum, " <" & asCols(j - 1) & "><![CDATA[";
Print #iFileNum, Trim(Cells(i, j).Value);
Print #iFileNum, "]]></" & asCols(j - 1) & ">"
DoEvents 'OPTIONAL
End If
Next j
Print #iFileNum, " </" & RowName & ">"
Next i
Print #iFileNum, "</" & sName & ">"
ExportToXML = True
ErrorHandler:
If iFileNum > 0 Then Close #iFileNum
Exit Function
End Function
To convert to a Sub that could be run from a button you would change it to:
Public Sub ExportToXML()
This will automatically change the last line to End Sub.
FullPath and RowName will no longer be passed as function-arguments, so would, presumably, need to be read from cells on a worksheet, or perhaps from two InputBoxes.
The Sub would no longer return a Boolean value, so whatever happens with this value would have to be converted to code within the same Sub (or possibly passed to another Sub).