I need a VBA routine to calculate the MD5 hash of a file's contents. I located some examples (e.g., here) but I found that they crashed when the filename contained certain Unicode characters, so I am trying to tweak the code to avoid that.
This code does not result in an error, but it also doesn't return the correct MD5 hash. What's wrong?
Public Function FileToMD5Hex(sFileName As String) As String
Dim enc
Dim bytes
Dim outstr As String
Dim pos As Integer
Set enc = CreateObject("System.Security.Cryptography.MD5CryptoServiceProvider")
'Convert the string to a byte array and hash it
bytes = GetFileBytes(sFileName)
bytes = enc.ComputeHash_2((bytes))
'Convert the byte array to a hex string
For pos = 1 To LenB(bytes)
outstr = outstr & LCase(Right("0" & Hex(AscB(MidB(bytes, pos, 1))), 2))
Next
FileToMD5Hex = outstr
Set enc = Nothing
End Function
Private Function GetFileBytes(path As String) As Byte()
Dim fso As Object
Set fso = CreateObject("scripting.FileSystemObject")
Dim fil As Object
Set fil = fso.GetFile(path)
' Dim fpga As Variant
GetFileBytes = fil.OpenAsTextStream().Read(fil.Size)
Set fil = Nothing
Set fso = Nothing
End Function
There are some chars sequences that Scripting.FileSystemObject can't process properly as TextStream.
Use ADODB.Stream ActiveX to retrieve array of bytes from file. It works perfectly with both text and binary types of data, also it allows to change charset of the string (FSO only works with ASCII and Unicode, and only with files).
Function GetFileBytes(strPath As String) As Byte()
With CreateObject("ADODB.Stream")
.Type = 1 ' adTypeBinary
.Open
.LoadFromFile (strPath)
GetFileBytes = .Read()
End With
End Function
Another one ActiveX processing binary data is SAPI.spFileStream. One of the most significant advantages - it allows to load only the part of the file to the memory (in some cases when comparing large files it can help drastically increase performance, checking md5 by chunks).
Function GetFileBytes(strPath As String) As Byte()
Dim arrContent As Variant
With CreateObject("SAPI.spFileStream")
.Open strPath, 0
.Read arrContent, CreateObject("Scripting.FileSystemObject").GetFile(strPath).Size
.Close
End With
GetFileBytes = arrContent
End Function
Related
I am trying to go through my text file and create a new file that will contain only the text I require. My current line looks like:
Car-1I
Colour-39
Cost-328
Dealer-28
Car-2
Colour-30
Cost-234
For each block of text I would like to read the first line, if the first line ends with an I, then read the next line, if that line contains a colour 39, then I would like to save the whole block of text to another file. If these two conditions aren't met, I dont want to save my values to the new text file.
Before anything about saving my values in classes are mentioned, these blocks of text can vary in size and values, so I dont always have a set range of values which is why i need to skip to the blank line
IO.File.WriteAllText("C:\Users\test2.txt", "") 'write to new file
Dim sKey As String
Dim sValue As Integer
For Each filterLine As String In File.ReadLines("C:\Users\test.txt")
sKey = Split(filterLine, ":")(0)
sValue = Split(filterLine, ":")(1)
If Not sValue.EndsWith("I") Then
ElseIf sValue.EndsWith("I") Then
End If
Next
Another method, using File.ReadLines to read lines of text from file. This method doesn't load all the text in memory, it reads from disc single lines of text, so it can also be useful when dealing with big files.
You could loop the IEnumerable collection it returns, but also use its GetEnumerator() method to control more directly when to move to the next line, or move more then one lines forward.
Its Enumerator.Current object returns the line of text currently read, Enumerator.MoveNext() moves to the next line.
A StringBuilder is used to store the strings when a match found. Strings are added to the StringBuilder object using its AppendLine() method.
This class is useful when dealing with strings that you need to store, compare and discard (or modify) quickly: since string are immutable, when you use String variables directly, especially in loops, you generate a whole lot of garbage that slows down any procedure quite a lot.
The blocks of text stored in the StringBuilder object are then written to a destination file using a StreamWriter with explicit encoding set to UTF-8 (writes the BOM). Its methods include asynchronous versions: WriteLine() can be replaced by awaitWriteLineAsync() to allow an async procedure.
Imports System.IO
Imports System.Text
Dim sourceFilePath = "<Path of the source file>"
Dim resultsFilePath = "<Path of the destination file>"
Dim sb As New StringBuilder()
Dim enumerator = File.ReadLines(sourceFilePath).GetEnumerator()
Using sWriter As New StreamWriter(resultsFilePath, False, Encoding.UTF8)
While enumerator.MoveNext()
If enumerator.Current.EndsWith("I") Then
sb.AppendLine(enumerator.Current)
enumerator.MoveNext()
If enumerator.Current.EndsWith("39") Then
While Not String.IsNullOrWhiteSpace(enumerator.Current)
sb.AppendLine(enumerator.Current)
enumerator.MoveNext()
End While
sWriter.WriteLine(sb.ToString())
End If
sb.Clear()
End If
End While
End Using
This will work:
Dim strFile As String = "c:\Test5\Source.txt"
Dim strOutFile As String = "c:\Test5\OutPut.txt"
Dim strOutData As String = ""
Dim SourceGroups As String() = Split(File.ReadAllText(strFile), vbCrLf + vbCrLf)
For Each sGroup As String In SourceGroups
Dim OneGroup() As String = Split(sGroup, vbCrLf)
If Strings.Right(OneGroup(0), 1) = "I" And (Strings.Right(OneGroup(1), 2) = "39") Then
If strOutData <> "" Then strOutData += (vbCrLf & vbCrLf)
strOutData += sGroup
End If
Next
File.WriteAllText(strOutFile, strOutData)
Something like this should work:
Dim base, i, c as Integer
Dim lines1$() = File.ReadLines("C:\Users\test.txt")
c = lines1.count
While i < c
if Len(RTrim(lines1(i))) Then
If Strings.Right(RTrim(lines1(i)), 1)="I" Then
base = i
i += 1
If Strings.Right(RTrim(lines1(i)), 2)="39" Then
While Len(RTrim(lines1(i))) 'skip to the next blank
i += 1
End While
' write lines1(from base to (i-1)) here
Else
While Len(RTrim(lines1(i)))
i += 1
End While
End If
Else
i += 1
End If
Else
i += 1
End If
End While
I have big problem in scraping webpage
I needed scraping img src adrress
but
result is
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII="
At first I didn't this but after I Knew that
but I can't decode
I try to search about base64 png image but I can't try to code at all.
I need your help
Give this a try. I wrote two functions DecodeBase64 and WriteByteArrayToFile. DecodeBase64 takes in a Base64 encoded string and returns the Byte() from this. WriteByteArrayToFile takes in a Byte() and a FilePath as string, and will write this Byte() to a file.
Update this section "YOURPATHGOESHERE\Picture.png" in the Example Sub to a valid file path on your computer, see if this code does what you are after. I'm getting a very small picture of what appears to be a square when running the code below.
Code
Option Explicit
'Decode Base64 string into a byte array
Public Function DecodeBase64(ByVal Base64String As String) As Byte()
With CreateObject("MSXML2.DOMDocument").createElement("b64")
.DataType = "bin.base64"
.Text = Base64String
DecodeBase64 = .nodeTypedValue
End With
End Function
'Take a byte array and write to a file
Public Sub WriteByteArrayToFile(FileData() As Byte, ByVal FilePath As String)
Dim FileNumber As Long: FileNumber = FreeFile
Open FilePath For Binary Access Write As #FileNumber
Put #FileNumber, 1, FileData
Close #FileNumber
End Sub
'Run from here
Sub example()
WriteByteArrayToFile DecodeBase64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGP6zwAAAgcBApocMXEAAAAASUVORK5CYII="), "YOURPATHGOESHERE\Picture.png"
End Sub
I want to translate a VB function to VBA. The function is using "System.Text.UTF8Encoding" and "System.Security.Cryptography.HMACSHA256"
Objects and their ".GetBytes" and ".ComputeHash" methods.
I already added "System" and "mscorlib.dll" referrences to the VBA code, but I'm receiving "Invalid procedure call or argument" error.
Here is my VB function:
Function HashString(ByVal StringToHash As String, ByVal HachKey As String) As String
Dim myEncoder As New System.Text.UTF8Encoding
Dim Key() As Byte = myEncoder.GetBytes(HachKey)
Dim Text() As Byte = myEncoder.GetBytes(StringToHash)
Dim myHMACSHA256 As New System.Security.Cryptography.HMACSHA256(Key)
Dim HashCode As Byte() = myHMACSHA256.ComputeHash(Text)
Dim hash As String = Replace(BitConverter.ToString(HashCode), "-", "")
Return hash.ToLower
End Function
And this is what I've already translated into VBA:
Function HashString(ByRef StringToHash As String, ByRef HachKey As String) As String
Dim myEncoder As Object
Dim myHMACSHA256 As Object
Dim Key As Byte
Dim Text As Byte
Dim HashCode As Byte
Dim hash As String
Set myEncoder = CreateObject("System.Text.UTF8Encoding")
Set myHMACSHA256 = CreateObject("System.Security.Cryptography.HMACSHA256")
Key = myEncoder.GetBytes(HachKey)
Text = myEncoder.GetBytes(StringToHash)
HashCode = myHMACSHA256.ComputeHash(Text)
hash = Replace(BitConverter.ToString(HashCode), "-", "")
HashString = hash.ToLower
End Function
Can anybody help on this? My first guess is that I'm using ".GetBytes" and ".ComputeHash" methods incorrectly
Thanks in advance
A working example to compute the HMACSHA256 with VBA:
Function ComputeHMACSHA256(key As String, text As String) As String
Dim encoder As Object, crypto As Object
Dim hash() As Byte, hmacsha As String, i As Long
' compute HMACSHA256
Set encoder = CreateObject("System.Text.UTF8Encoding")
Set crypto = CreateObject("System.Security.Cryptography.HMACSHA256")
crypto.key = encoder.GetBytes_4(key)
hash = crypto.ComputeHash_2(encoder.GetBytes_4(text))
' convert to an hexa string
hmacsha = String(64, "0")
For i = 0 To 31
Mid$(hmacsha, i + i + (hash(i) > 15) + 2) = Hex(hash(i))
Next
ComputeHMACSHA256 = LCase(hmacsha)
End Function
Sub UsageExample()
Debug.Print ComputeHMACSHA256("abcdef", "12345")
End Sub
When used via COM in order to support overloading .Net functions have implementations based on Name_n. As GetBytes is overloaded you need GetBytes_4() which is the overload that accepts a string and _2 for ComputeHash()
Function HashString(ByRef StringToHash As String, ByRef HachKey As String) As String
Dim myEncoder As Object
Dim myHMACSHA256 As Object
Dim Key() As Byte '// all need to be arrays
Dim Text() As Byte
Dim HashCode() As Byte
Dim hash As String
Set myEncoder = CreateObject("System.Text.UTF8Encoding")
Set myHMACSHA256 = CreateObject("System.Security.Cryptography.HMACSHA256")
Key = myEncoder.GetBytes_4(HachKey)
Text = myEncoder.GetBytes_4(StringToHash)
HashCode = myHMACSHA256.ComputeHash_2(Text)
Dim i As Long
For i = 0 To UBound(HashCode)
Debug.Print Format$(Hex(HashCode(i)), "00")
Next
End Function
?HashString("qwe", "rty")
80
D5
22
5D
83
06
...
i want to use split function for splitting string into two parts.The question is how can i save the first part and second part into two different string array for further use?
The text i'm going to process is Name,filepath
Dim clientArr() as string
Dim FileArr() as string
FileOpen(1, strpath & "Location.txt", OpenMode.Input)
Do Until EOF(1)
Templine = LineInput(1)
Dim DataArr() As String = Templine.Split(",")
ClientArr = DataArr(0)
FileArr = DataArr(1)
loop
The error says that string cannot be convented into 1 D array
Thank you
It is the 21st Century and along with flying cars and robot dogs we have a easy way to keep 2 large sets of related data together in a single container, but able to be referenced individually:
Class myFileParts
public Property FileName As String
public Property FilePath As String
Public Sub New(fName as string, fPath as String)
FileName = fName
FilePath = fPath
End Sub
End Class
' a container to hold lots of these
Friend myFiles As New List(Of myFileParts)
' filling it up:
Dim TempLine As String
Dim myF As myFileParts
Do Until EOF(1)
Templine = LineInput(1)
Dim DataArr() As String = Templine.Split(",")
' create a file item
f = New myFileParts(DataArr(0),DataArr(1))
' add to container
myFiles.Add(f)
loop
to use one: (N is the index of the desired file info):
Dim thisFile As string = MyFiles(N).FileName
Dim thisPath As string = MyFiles(N).FilePath
to print them all:
For each f As myFileParts in myFiles
Console.WriteLine(String.Format("Path = {0}; Name = {1}",
f.FilePath, f.FileName)
Next f
The problem is that you declare ClientArr and FileArr as string arrays, which is what you want, but then you try to assign them a string value. DataArr(0) is a string value. You need to put that vcalue into one of the array elements in ClientArr. E.g. CleintArr(5) = DataArr(0).
If you knew the exact number of line in the file before the loop then you could declare the array size for CleintArr and FileArr. Then you would use an index that gets increments in the loop to set the proper array element.
But here is a better approach that will work without knowing the size of the file beforehand. This uses string collections to accumulate the clients and files, and then it converts them to an array once the file has been read.
Dim clientArr() As String
Dim FileArr() As String
Dim ClientList As New List(Of String)
Dim FileList As New List(Of String)
FileOpen(1, strpath & "Location.txt", OpenMode.Input)
Do Until EOF(1)
Templine = LineInput(1)
Dim DataArr() As String = Templine.Split(",")
ClientList.Add(DataArr(0))
FileList.Add(DataArr(1))
Loop
clientArr = ClientLIst.ToArray()
FileArr = FileList.ToArray()
Do you know how many lines are in the file before you start? If not, you could do something like this using the List class:
Dim clientPart As List(Of String) = New List(Of String)()
Dim filePart As List(Of String) = New List(Of String)()
FileOpen(1, strpath & "Location.txt", OpenMode.Input)
Do Until EOF(1)
Templine = LineInput(1)
Dim DataArr() As String = Templine.Split(",")
clientPart.Add(DataArr(0))
filePart.Add(DataArr(1))
Loop
Dim clientPartStringArray As String() = clientPart.ToArray() 'If you really want an array
If you do, then you need to assign each split to an element of your clientArr and fileArr like:
Dim numLinesInFile As Integer 'If you know this value
Dim curIndex As Integer = 0
Dim clientArray(numLinesInFile) As String
Dim fileArray(numLinesInFile) As String
FileOpen(1, strpath & "Location.txt", OpenMode.Input)
Do Until EOF(1)
Templine = LineInput(1)
Dim DataArr() As String = Templine.Split(",")
clientArray(curIndex) = DataArr(0)
fileArray(curIndex) = DataArr(1)
curIndex = curIndex + 1
Loop
Note the differences in your code in terms of:
We declare the arrays clientArray and fileArray with a specific size (you'll need to know this in advance - if you don't/can't know this consider using a collection like a List as previously alluded to)
On each iteration we assign the strings you get from the Split into a specific index of the array denoted by curIndex
We have to increment curIndex by 1 each time so that we write to empty slots in clientArray and fileArray...otherwise you'd just write to the same index (0) each time
This is my code to search for a string for all files and folders in the drive "G:\" that contains the string "hello":
Dim path = "g:\"
Dim fileList As New List(Of String)
GetAllAccessibleFiles(path, fileList)
'Convert List<T> to string array if you want
Dim files As String() = fileList.ToArray
For Each s As String In fileList
Dim text As String = File.ReadAllText(s)
Dim index As Integer = text.IndexOf("hello")
If index >= 0 Then
MsgBox("FOUND!")
' String is in file, starting at character "index"
End If
Next
This code will also results in memory leak/out of memory exception (as I read file as big as 5GB!). Perhaps it will bring the whole file to the RAM, then went for the string check.
Dim text As String = File.ReadAllText("C:\Users\Farizluqman\mybigmovie.mp4")
' this file sized as big as 5GB!
Dim index As Integer = text.IndexOf("hello")
If index >= 0 Then
MsgBox("FOUND!")
' String is in file, starting at character "index"
End If
But, the problem is: This code is really DANGEROUS, that may lead to memory leak or using 100% of the RAM. The question is, is there any way or workaround for the code above? Maybe chunking or reading part of the file and then dispose to avoid memory leak/out of memory? Or is there any way to minimize the memory usage when using the code? As I felt responsible for other's computer stability. Please Help :)
You should use System.IO.StreamReader, which reads line by line instead all the lines at the same time (here you have a similar post in C#); I personally never use ReadAll*** unless under very specific conditions. Sample adaptation of your code:
Dim index As Integer = -1
Dim lineCount As Integer = -1
Using reader As System.IO.StreamReader = New System.IO.StreamReader("C:\Users\Farizluqman\mybigmovie.mp4")
Dim line As String
line = reader.ReadLine
If (line IsNot Nothing AndAlso line.Contains("hello")) Then
index = line.IndexOf("hello")
Else
If (line IsNot Nothing) Then lineCount = line.Length
Do While (Not line Is Nothing)
line = reader.ReadLine
If (line IsNot Nothing) Then
lineCount = lineCount + line.Length
If (line.Contains("hello")) Then
index = lineCount - line.Length + line.IndexOf("hello")
Exit Do
End If
End If
Loop
End If
End Using
If index >= 0 Then
MsgBox("FOUND!")
' String is in file, starting at character "index"
End If