I have arrays of integers, each is a ASCII code representing a single byte of a string.
I can generate a string from the array like this:
Sub BytesToString()
Dim myArr(): myArr = Array(84, 104, 105, 115, 32, _
105, 115, 32, 97, 32, 116, 101, 115, 116, 33)
Dim c As Variant, myStr As String
For Each c In myArr
myStr = myStr & Chr(c)
Next c
MsgBox myStr
End Sub
...but I feel like this isn't "the right way" to do this, especially since repeated conversions may be needed. Array length will vary.
Is there a built-in or more efficient method to produce the string with VBA?
One method is to create a string variable and then append the byte value to the string variable with the help of + operator. This will directly convert the byte value to a string and add it in the string variable. The simplest way to do so is using valueOf() method of String class in java.
For text or character data, we use new String(bytes, StandardCharsets. UTF_8) to convert the byte[] to a String directly. However, for cases that byte[] is holding the binary data like the image or other non-text data, the best practice is to convert the byte[] into a Base64 encoded string.
So below code can also be used to convert byte array to String in Java. String str = new String(byteArray, StandardCharsets. UTF_8); String class also has a method to convert a subset of the byte array to String.
Turns out, this is one of those rare times where the solution was so simple it was overlooked by several people, including myself.
In VBA, Byte Arrays are special because, unlike arrays of other data types, a string can be directly assigned to a byte array.
In VBA, Strings are UNICODE strings, so when one assigns a string to a byte array then it stores two digits for each character. The first digit will be the ASCII value of the character and next will be 0.
(Source: VBA Trick of the Week: Byte Arrays in VBA - Useful Gyaan)
A couple code samples will likely demonstrate better than I can explain:
Sub Demo1()
Dim myArr() As Byte, myStr As String
myStr = "Hi!"
myArr() = myStr
Debug.Print "myStr length: " & Len(myStr) 'returns "3"
Debug.Print "Arr bounds: " & LBound(myArr) &"to"& UBound(myArr) 'returns "0 to 5"
myStr = myArr
Debug.Print myStr 'returns "Hi!"
End Sub
In the above case the string's length is 3 so the array’s size will be 6. Values will be stored in the following way:
myArr(0) = 72 ' ASCII : code for 'H'
myArr(1) = 0 ' ASCII 'null' character
myArr(2) = 105 ' ASCII : code for 'i'
myArr(3) = 0 ' ASCII 'null' character
...etc...
The
StrConv
function can be used if one wants to remove these zeros. In this case it will store ASCII values only.myByteArr() = StrConv("StackOverflow", vbFromUnicode)
Just like a string can be directly assigned to a byte array, a byte array can also be directly assigned to a string. In above example if one assigns
myArr
to a string then it will store the same value that has been assigned to the array.
When the array is populated element-by-element - or, in my case, from a speedy file operation (see below) - an extra step of conversion with StrConv
is required.
Sub Demo2()
Dim myArr(0 To 5) As Byte, myStr As String
myArr(0) = 104: myArr(1) = 101: myArr(2) = 108
myArr(3) = 108: myArr(4) = 111: myArr(5) = 33
Debug.Print "myArr bounds: " & LBound(myArr) &"to"& UBound(myArr) 'returns "0 to 5"
'since the array was loaded byte-by-byte, we can't "just put back":
myStr = myArr()
Debug.Print myStr 'returns "???" (unprintable characters)
Debug.Print "myStr length: " & Len(myStr) 'returns "3"
'using `StrConv` to allow for 2-byte unicode character storage
myStr = StrConv(myArr(), vbUnicode)
Debug.Print myStr 'returns "hello!"
Debug.Print "myStr length: " & Len(myStr) 'returns "6"
End Sub
I have large text files that I been wanting parse/analyze with VBA, but couldn't find a method that wasn't painfully slow in either the loading or the character-by-character parsing.
As an example, today I managed to load a quarter-gigabyte file in 1/10th of a second, and parsed it into a second Byte Array:
Dim bytes() As Byte
Open myFileName For Binary Access Read As #1
ReDim bytes(LOF(1) - 1&)
Get #1, , bytes
Close #1
For x = LBound(arrOut) To UBound(arrOut)
Select Case bytes(x)
(..and if I want the character)
bytes2(y) = bytes(x)
y = y + 1
End Select
Next x
ReDim Preserve bytes2(LBound(bytes2) To y - 1)
txtIn = StrConv(bytes2, vbUnicode)
...and I had my completed string in under 5 seconds total. (Hooray!)
If you are curious about different ways, you can always count on .NET libraries! In this case, you have to add reference to mscorlib.dll
in your VBA editor and then use this code:
Option Explicit
Sub BytesToString()
Dim en As ASCIIEncoding
Set en = New ASCIIEncoding
Dim myArr(0 To 2) As Byte
myArr(0) = 72
myArr(1) = 105
myArr(2) = 33
MsgBox en.GetString(myArr)
End Sub
Since you are looking for built-in functions, that is one. But it's inefficient. Approximately takes 10 times longer than your custom decoder as I checked.
UPDATE
However, when I check this in .NET (C#), it is approximately 20 times faster than custom approach presented by OP.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With