Read file to string in VBA - vba

I'm working on a sub that will use an HTTP post to upload a file to a server. I've already worked out all of the code to actually sent the post request, but I can't figure out how to get the file into the appropriate format. The server is expecting the file's bytes as a string, but no matter what approach I take to creating the string the values don't match up with what's expected.
Here is the code I currently have:
Sub Test()
Dim FileNum As Integer
Dim TestString As String
Dim TestBytes() As Byte
Dim TestByte As Variant
FileNum = FreeFile
Open ActiveDocument.Path & "\" & ActiveDocument.name For Binary Access Read As #FileNum 'Open document to write to string
ReDim TestBytes(LOF(FileNum) - 1)
Get FileNum, , TestBytes
Close FileNum
For Each TestByte In TestBytes
TestString = TestString & Hex(TestByte)
Next TestByte
Debug.Print TestString
End Sub
The output looks something like this (truncated, as the full string is obviously quite long):
504B3414060800021076DD8E8C9D130825B436F6E74656E745F54797065735D2E786D6C20...
The problem is, the example output I have says it should look like this:
UEsDBBQABgAIAAAAIQCuTjGvewEAAAIGAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAACA...
I assumed the problem was that my test code is trying to encode the bytes as hex values, whereas the example is obviously not in hex, but when I just try to output the file's bytes directly as a string I get a lot of invalid characters. Here's the code:
Sub Test()
Dim FileNum As Integer
Dim TestString As String
Dim TestBytes() As Byte
Dim TestByte As Variant
FileNum = FreeFile
Open ActiveDocument.Path & "\" & ActiveDocument.name For Binary Access Read As #FileNum 'Open document to write to string
TestString = String$(LOF(FileNum), Chr(32)) 'Fill string with blank space to set string length
Get FileNum, , TestString 'Write binary data from file to string
Debug.Print TestString
End Sub
And here's the output:
PK ! vÝŽŒ p  ...
Is there something I'm missing as to how I can encode the bytes to get output that's encoded the same as the example output? When performing a similar operation in another language (e.g. Java using readFileToString), how is the string encoded?

Using Alex K.'s advice in the comments above, and the Base64 encoding function found here, I came to this solution:
Sub Test()
Dim FileNum As Integer
Dim TestString As String
Dim TestBytes() As Byte
Dim TestByte As Variant
FileNum = FreeFile
Open ActiveDocument.Path & "\" & ActiveDocument.name For Binary Access Read As #FileNum 'Open document to write to string
TestString = String$(LOF(FileNum), Chr(32)) 'Fill string with blank space to set string length
Get FileNum, , TestString 'Write binary data from file to string
TestString = EncodeBase64(TestString)
Debug.Print TestString
End Sub
Function EncodeBase64(text As String) As String
Dim arrData() As Byte
arrData = StrConv(text, vbFromUnicode)
Dim objXML As MSXML2.DOMDocument
Dim objNode As MSXML2.IXMLDOMElement
Set objXML = New MSXML2.DOMDocument
Set objNode = objXML.createElement("b64")
objNode.DataType = "bin.base64"
objNode.nodeTypedValue = arrData
EncodeBase64 = objNode.text
Set objNode = Nothing
Set objXML = Nothing
End Function
Which outputs the correct string:
UEsDBBQABgAIAAAAIQCuTjGvewEAAAIGAAATAAgCW0NvbnRlbnRfVHlwZXNdLnhtbCCiBAIooAACA...

Related

Having a bad file name or number

I'm quite new with VBA and am trying to use a code that I found online. However, when I run the code I get the error that I'm having a bad file name or number. When I look in the Locals window the path and file name seem to be expressed. Does anyone have an idea how to fix this?
Public Sub Create_KMZ_File_Filled_Circles()
Dim KMLfileName As String, KMZfullName As String
Dim KMLfullName As Variant, ZIPfullName As Variant 'must be Variants for WShell functions
Dim folderPath As String
Dim data As Variant
Dim WShell As Object
Dim fn As Integer
Dim Circle_Coords() As Double
Dim i As Long, r As Long
folderPath = ThisWorkbook.Path
If Right(folderPath, 1) <> "\" Then folderPath = folderPath & "\"
KMLfileName = "Circles Filled.kml"
KMLfullName = folderPath & KMLfileName
KMZfullName = Replace(KMLfullName, ".kml", ".kmz")
'Put sheet data in array
data = ThisWorkbook.Worksheets("Sheet1").UsedRange
fn = FreeFile
Open KMLfullName For Output As #fn
I'm working with sharepoint - could that create the issue?

How to get the number of lines of data in CSV file in VBA

I tried to get the number of lines of data in several CSV files in VBA.
Here is the code.
Sub Woo_Products()
Dim fso As New FileSystemObject
Dim flds As Folders
Dim fls As Files
Dim strText As String
Dim i As Integer
Dim j As Integer
Dim k As Integer
Dim extfind As String
Dim FilePath As String
Dim sLineOfText As String
On Error Resume Next
Workbooks.Open Filename:="F:\Work\scrape\" & "woocommerce-products.csv", UpdateLinks:=3
Set fls = fso.getfolder("C:\Users\star\Downloads").Files
k = 2
For Each f In fls
strText = f.Name
extfind = Right$(strText, Len(strText) - InStrRev(strText, "."))
If extfind = "csv" Then
FilePath = "C:\Users\star\Downloads\" & strText
Open FilePath For Input As #1
i = 0
Do Until EOF(1)
Line Input #1, sLineOfText
If sLineOfText <> "" Then i = i + 1
Loop
Close #1
End If
Next
Windows("woocommerce-products.csv").Activate
ActiveWorkbook.Save
ActiveWorkbook.Close
End Sub
But I am getting the same count for each file.
Of course, each file has different lines of data.
Hope to help me for this.
If all you need is a line count, I would write a function to return the count.
Function getFileLineCount(FullFileName As String, Optional LineDelimiter As String = vbNewLine) As Long
Dim text As String
Dim fileNo As Integer, n As Long
fileNo = FreeFile
Open FullFileName For Input As #fileNo
Do Until EOF(1)
Line Input #1, text
n = n + 1
Loop
Close #fileNo
getFileLineCount = n
End Function
Another approach using FileSystemObject:
Public Function GetLineCount(ByVal Path As String) As Long
With CreateObject("Scripting.FileSystemObject")
GetLineCount = UBound(Split(.OpenTextFile(Path, 1).ReadAll, vbNewLine)) + 1
End With
End Function
You'll need to add the references (Tools --> References)
Microsoft Scripting Runtime
Microsoft VBScript Regular Expressions 5.5
This will count "Return & NewLine" characters in the file.
Private Function LineCount(ByVal PathFile As String) As Long
Dim sData As String
Dim oFile As New FileSystemObject
sData = oFile.OpenTextFile(PathFile, ForReading).ReadAll
Dim oRegX As New RegExp
oRegX.Pattern = "\r\n"
oRegX.Global = True
LineCount = oRegX.Execute(sData).Count + 1
Set oRegX = Nothing
Set oFile = Nothing
End Function
i = ActiveWorkbook.ActiveSheet.Cells(ActiveWorkbook.ActiveSheet.Rows.Count, 1).End(xlUp).Row
It's working so well.

VBA Exact Text Match

I am attempting to pull a column of letter codes using InStr from existing text files. I would like to allow this macro to be flexible enough to allow the option to manually input the letter code being searched.
Where I am running into an issue is when common numbers are in the letter code (i.e. C4 and C45). Originally the code was written to search for 2 specific letter codes, and I was hoping to be able to replace those 2 identifiers with a link to 2 cells.
No such luck, as I keep getting responses that are not exact. Please see the code below:
Private Sub CmdLettersGetfile_Click()
Dim objFSO As Object
Dim myDir As String, myList()
Dim objFolder As Object
Dim objFile As Object
Dim i As Integer
Dim sFolder As String
Dim fd As Office.FileDialog
Dim row As Long
Dim row1 As Long
Dim FCount As Integer
Dim FCount1 As Integer
Dim Val As String
Dim Pos As Integer
Dim Last1 As Long 'Mark the last row
Dim Start2 As Long
Dim Last2 As Long 'Mark the last row
Set fd = Application.FileDialog(msoFileDialogFilePicker)
For Each cn In ThisWorkbook.Connections
cn.Delete
Next cn
With ActiveSheet
Lastrow = Sheets("MAIN").Cells(.Rows.Count, "E").End(xlUp).row
End With
'
Worksheets("REPORT").Range("A6:AA1000000").ClearContents
Worksheets("REPORT").Range("A6:AA1000000").ClearFormats
row1 = 6 'Start of REPORT ROW
For row = 12 To Lastrow
sFile = Worksheets("MAIN").Cells(row, "E").Value
Pos = InStr(1, sFile, "org")
If Pos = 0 Then
Val = Worksheets("MAIN").Cells(9, "H")
Else
Val = Worksheets("MAIN").Cells(10, "H")
End If
Pos = 0
Dim strFilename As String
Dim strTextLine As String
Dim iFile As Integer
iFile = FreeFile
strFilename = folderName & "\" & sFile
Open strFilename For Input As #iFile
Last1 = 0
Last2 = 0
Do Until EOF(1)
Line Input #1, strTextLine
FCount = FCount
Pos = InStr(1, strTextLine, Val)
I cleaned up some of the irrelevant code in an attempt to save space and focus on the lines where I am having the issue.
********Update (26-Apr-2018 # 18:12 EST):
When I tried to format the Val to be one uppercase letter and 2 numbers
Dim Val As String: Val = "([A-Z]{1})([0-9]{1})([0-9]{1})"
I get the following:
Resulting data
Column I is the letter code that I need to get narrowed down.
*********Update 2 (27-Apr-2018 # 14:37 EST):
Expanded resulting data
I was able to mass redact what I needed to. I hope this offers more insight into what I'm looking to do.

Parsing a byte array with VBA using MS Access

I have a 100 million character text file that I'm trying to import into an MS Access database. The file does not have any linefeeds so it is just one giant line of text. I tried loading it into a string variable but that did not work because of the size. I then successfully loaded it into a byte array, but I'm unsure of how to parse it the way I need it. The file has fixed length records, but has more than one type of record. One type may be 180 characters of data and 220 characters of filler, and another may be 100 characters of data and 300 characters of filler. I would like to the different record types into separate tables. I was thinking of rewriting the data to a new text file in 400 character chunks, and then maybe using Trim to get just the data I need with no filler. From there I could test the line length and import.
Here is what I have, but it does not work.
Public Sub modMain_ParseAQTFiles()
Dim bytFile(400) As Byte
Dim intFileIn As Integer
Dim intFileOut As Integer
Dim intFileOut1 As Integer
Dim intFileOut2 As Integer
Dim intFFIn As Integer
Dim intFFOut As Integer
Dim lngBytePos As Long
Dim dblStartChar As Double
Dim lngNoRecs As Long
Dim lngIndex As Long
Dim strFileIn As String
Dim strFileOut1 As String
Dim strFileOut2 As String
Dim strLineOfText As String
Dim strTextLine As String
Dim strUserName As String
'Get username
strUserName = Environ("Username")
'Set file paths
strFileIn = "C:\Users\" & strUserName & "\Desktop\Pooltalk\aqt.txt"
strFileOut1 = "C:\Users\" & strUserName & "\ Desktop\Pooltalk\ AQT_Quartiles_Header-out.txt"
strFileOut2 = "C:\Users\" & strUserName & "\Desktop\Pooltalk \AQT_Quartiles_Detail-out.txt"
'Reads data into byte array
intFFIn = FreeFile
intFFOut = FreeFile
dblStartChar = 1
Open strFileIn For Binary Access Read As #intFFIn
lngNoRecs = LOF(intFFIn) / 400
For lngIndex = 1 To lngNoRecs
Get #intFFIn, dblStartChar, bytFile
strLineOfText = StrConv(bytFile, vbFromUnicode)
Open strFileOut For Binary Access Write As #intFFOut
Put intFFOut, dblStartChar, strLineOfText & vbCrLf
Debug.Print strLineOfText
dblStartChar = dblStartChar + 400
Next lngIndex
Close #intFFIn
Close #intFFOut
End Sub
I would be happy to hear if anyone has any advice to get this working. Thanks.
EDIT:
Here is one record type:
1004569 AS20170431360FCE319840701
34 characters of data and 366 blanks
Here is the second record type:
200456906875{06875{06875{06875{06875{06875{07I07I07I07I07I07I40B40B40B40B40B40B0000630000{0000630000{0000630000{0000630000{0000630000{0000630000{48{48{48{48{48{48{05926{05926{05926{05926{05926{05926{01250{01250{01250{01250{01250{01250{06875{06875{06875{06875{06875{06875{16875{16875{16875{16875{16875{16875{
307 characters and 93 blanks.
Here is my final code:
Public Sub modMain_ParseAQTFiles()
Dim intFileIn As Integer
Dim intFileOut1 As Integer
Dim intFFIn As Integer
Dim intFFOut As Integer
Dim lngNoRecs As Long
Dim lngIndex As Long
Dim strFileIn As String
Dim strFileOut1 As String
Dim strUserName As String
Dim strRecord As String
Dim dblStartChar As Double
Dim lngCharNo As Long
strUserName = Environ("Username")
'Set file paths
strFileIn = "C:\Users\" & strUserName & "\Desktop\Pooltalk\aqt.txt"
strFileOut1 = "C:\Users\" & strUserName & "\Desktop\Pooltalk\AQT_Parsed.txt"
strRecord = Space$(400)
dblStartChar = 1
'Reads data into byte array
intFFIn = FreeFile
Open strFileIn For Binary Access Read As #intFFIn
intFFOut = FreeFile
Open strFileOut1 For Binary Access Write As #intFFOut
'Find number of records
lngNoRecs = LOF(intFFIn) / 400
For lngIndex = 1 To lngNoRecs
Get #intFFIn, dblStartChar, strRecord
strRecord = Trim(strRecord)
Put intFFOut, , strRecord & vbCrLf
dblStartChar = dblStartChar + 400
strRecord = Space$(400)
Next lngIndex
Close #intFFIn
Close #intFFOut
MsgBox "Done!"
End Sub
If all records are 400 characters long, I would read them directly into a string variable of that length.
Dim strRecord As String
Dim x As Long
' Get reads as many characters as are in the target variable
strRecord = Space$(400)
Get #intFFIn, dblStartChar, strRecord
' Find first 0-byte character
x = Instr(strRecord, Chr$(0))
' and trim off the fillers
strRecord = Left$(strRecord, x-1)
See https://msdn.microsoft.com/VBA/Language-Reference-VBA/articles/get-statement at the bottom (before the example).

How to open text files and move them to a folder using VBA?

I need some help with a project in which I must open a list of text files, find a pattern in their contents and then move to other folders according to the pattern.
For example, in a list of text files, I must find which of them have the word "blue" written inside and them move only those to another folder named "Blue".
I was tring to do it using the command FileSystemObject, but I was kind of lost.
Thanks a lot in advance!!
Dim sDir As String
Dim sPath As String
Dim sPattern as String
Dim sReadedData as String
dim sDestiny as string
dim sPathDestiny as string
Dim fso As Object
Set fso = VBA.CreateObject("Scripting.FileSystemObject")
sPath$ = "c:\YourFolder"
sDir$ = Dir(sPath, vbDirectory)
sPattern= "abcdefg"
sDestiny="c:\DestinyFolder"
If sDir = "" Then
MsgBox "Path " & sDir & " Not Found"
End
End If
sDir$ = Dir(sPath & "\*.txt")
Do Until sDir = ""
sPathDestiny=Replace(sDir, sPath, sDestiny)
Open sDir$ For Input As #1
do until EOF(1)
Input #1, sReadedData
loop
if InStr(sReadedData, sPattern)>0 then
Call fso.CopyFile(sDir, sPathDestiny)
end if
Loop
This is the main idea. Play with it.