I am trying to use the StrConv function to convert the letter H to its decimal value 72 and then from 72 back again to H.
Public Sub Testing()
Dim Text As String, Back As String, Deci() As Byte
Text = "H"
Deci = StrConv(Text, vbFromUnicode)
Debug.Print Deci(0)
Back = StrConv(Deci(0), vbUnicode)
Debug.Print Back
End Sub
In the above example I am using StrConv(Text, vbFromUnicode) to get the decimal value 72 which works great.
However when I then try to go from 72 to H by using StrConv(Deci(0), vbUnicode) I do not get H but 72 with a strange character inbetween.
Is it not correct to use the argument vbUnicode here? What am I doing wrong?
Related
In Excel VBA (2019) I am trying to write out a bridge deal file format of type .bri
The specifications for .bri require repeated (depending how many boards in the total deal) sets of
The 39 ASCII codes for cards in each board
10 normally ASCII spaces
22 further ASCII spaces
18 further bytes of null-value (all bits set to 0)
as one continuous line in the file.
An example sample of code that I have tried writes just 1 board via:
Type BriType
boardVar As String
byteArr(17) As Byte
End Type
Sub TestBriWrite()
Dim i, j, k, l, m As Integer
Dim FileName As String, fileNo As Integer
Dim FileLoc As Variant
Dim filler As String
Dim BriVar As BriType
BriVar.boardVar = "010203040506070809101112131415161718192021222324252627282930313233343536373839 "
For m = 0 To 17
BriVar.byteArr(m) = 0
Next m
FileLoc = Application.GetSaveAsFilename(Title:="Save the Deal File as xxxxx.bri")
fileNo = FreeFile
Open FileLoc For Binary As #fileNo
Put #fileNo, 1, BriVar
Close #fileNo
End Sub
This seems to be close to what I want but each board entry is preceeded by "n " in the output file. Can anyone suggest how I can achieve what I am trying to do?
Sorry, this is my first post on the board so I may not have followed all of the rules.
Since BriVar.boardVar is declared as a variable length string, the first two bytes are being used to keep track of its length for subsequent reading.
In your example, you're assigning your variable a 110 character string. So it uses two bytes to store that value in your file. The first byte has the value of 110, and the second byte has the value of 0.
And so when you open your file in a text editor, you're seeing the character representation of the ASCII values 110 and 0, which are the letter 'n' and the null character.
You can avoid these two bytes by declaring a fixed length string instead. For example, let's say that your string will always be 110 characters in length. Then you would declare your string as follows...
Type BriType
boardVar As String * 110 '<--- fixed length string
byteArr(17) As Byte
End Type
By the way, you should amend your code so that it exits the sub when the user is prompted to save the file, and clicks on Cancel....
FileLoc = Application.GetSaveAsFilename(Title:="Save the Deal File as xxxxx.bri")
If FileLoc = False Then Exit Sub '<-- user cancelled
So here's your macro amended as above...
Type BriType
boardVar As String * 110
byteArr(17) As Byte
End Type
Sub TestBriWrite()
Dim i, j, k, l, m As Integer
Dim FileName As String, fileNo As Integer
Dim FileLoc As Variant
Dim filler As String
Dim BriVar As BriType
FileLoc = Application.GetSaveAsFilename(Title:="Save the Deal File as xxxxx.bri")
If FileLoc = False Then Exit Sub
BriVar.boardVar = "010203040506070809101112131415161718192021222324252627282930313233343536373839 "
For m = 0 To 17
BriVar.byteArr(m) = 0
Next m
fileNo = FreeFile
Open FileLoc For Binary As #fileNo
Put #fileNo, 1, BriVar
Close #fileNo
End Sub
I am pushing a set of bytes from an external device to a COM port. The resultant input needs to be put in a cell in Excel, and converted to hex. I have tried various sets of tools, but none show me any results in Excel.
I have tried some VBA extensions, but they were all paid for, have tried some terminal tools as well. Current VBA tool code is shown below. I cannot get it to show anything in an excel cell either. Results just show in immediate logger.
Private Sub StrokeReader1_CommEvent(ByVal Evt As StrokeReaderLib.Event, ByVal data As Variant)
Select Case Evt
Case EVT_DISCONNECT
Debug.Print "Disconnected"
Case EVT_CONNECT
Debug.Print "Connected"
Case EVT_DATA
buf = (StrokeReader1.Read(Text)) 'Use BINARY to receive a byte array
Debug.Print buf
End Select
End Sub
'Use this to connect and set the port properties from the code
Sub connect()
StrokeReader1.Port = 3
StrokeReader1.BaudRate = 19200
StrokeReader1.PARITY = NOPARITY
StrokeReader1.STOPBITS = ONESTOPBIT
StrokeReader1.DsrFlow = False
StrokeReader1.CtsFlow = False
StrokeReader1.DTR = False
StrokeReader1.RTS = False
StrokeReader1.Connected = True
If StrokeReader1.Error Then
Debug.Print StrokeReader1.ErrorDescription
End If
End Sub
'Use this to send data to the remote device
Sub send()
StrokeReader1.send "ABCD" 'A text string
Dim x(3) As Byte 'A byte array
x(1) = 1
x(2) = 2
x(3) = 3
StrokeReader1.send x
End Sub
Expected results: AA 00 00 22 00 03 00 00 03 2B 01 E1 35
Actual result: ª " Ö $$
Case EVT_DATA
buf = (StrokeReader1.Read(Text)) 'Use BINARY to receive a byte array
Debug.Print buf
You're getting a byte array, which as far as VBA is concerned, is indistinguishable from a string - that's the only reason Debug.Print buf isn't throwing a type mismatch error - because any other array simply has no way to represent itself as a string, so you can't debug-print an array.
What you need is to iterate the bytes in the array, use the Hex function to get the hexadecimal representation of each byte, and concatenate them into a legible string.
Something like this:
Private Function ToHexString(ByRef buffer As Variant) As String
'note: buffer array is wrapped in a Variant, could be passed ByVal
'first allocate/size a dynamic array to hold our results;
'by sizing from LBound(buffer) to UBound(buffer), we get
'an array that's sized exactly the same as the buffer array.
ReDim bytes(LBound(buffer) To UBound(buffer))
'now iterate all items in the array
Dim i As Long
For i = LBound(buffer) To UBound(buffer)
'store the hex representation of the byte at index i into our hex-bytes array
bytes(i) = Hex(buffer(i))
Next
'return the joined hex-bytes array, using a space to separate the individual hex-bytes
ToHexString = Strings.Join(bytes, " ")
End Function
Now you can do Debug.Print ToHexString(buf) and that should yield the expected output.
If you want that output to a cell, you need to get a Range object from a specific Worksheet. For example if you want to write to cell A1 of whatever the active sheet is:
ActiveSheet.Range("A1").Value = ToHexString(buf)
Or
ActiveSheet.Cells(1, 1).Value = ToHexString(buf)
Good day, experts. I'm getting 16 chars string value from uart, like this "0000000000001110", then i want to add space every 2 chars: "00 00 00 00 00 00 11 10". What i was thinking it's making for-next loop count every 2 chars in a "data", then add a space between it. But i'm really have no ideas how to accomplish it. That's what i tried so far:
Dim i As Long
Dim data As String = "0000000000001110"
For i = 0 To Len(data) Step 2 ' counting every 2 chars
data = Mid(data, i + 1, 2) ' assign 2 chars to data
' stucked here
Next i
Any input appreciated, thanks.
You can use a StringBuilder and a backwards loop:
Dim data As String = "0000000000001110"
Dim builder As New StringBuilder(data)
Dim startIndex = builder.Length - (builder.Length Mod 2)
For i As int32 = startIndex to 2 Step -2
builder.Insert(i, " "c)
Next i
data = builder.ToString()
The conditional operator(in VB If) using the Mod is used to find the start index(loooking from the end of the string). Because it will be different if the string has an even/odd number of characters. I use the backwards loop to prevent the problem that inserting characters changes the size of the string/StringBuilder, hence causing wrong indexes in the for-loop.
Here is an extension method that encapsulates the complexity and improves reusability:
Imports System.Runtime.CompilerServices
Imports System.Text
Module StringExtensions
<Extension()>
Public Function InsertEveryNthChar(str As String, inserString As String, nthChar As Int32) As String
If string.IsNullOrEmpty(str) then Return str
Dim builder as New StringBuilder(str)
Dim startIndex = builder.Length - (builder.Length Mod nthChar)
For i As int32 = startIndex to nthChar Step -nthChar
builder.Insert(i, inserString)
Next i
return builder.ToString()
End Function
End Module
Usage:
Dim data = "00000000000011101"
data = data.InsertEveryNthChar("[foo]", 3) ' 000[foo]000[foo]000[foo]000[foo]111[foo]01
I know you have already accepted an answer, however you could also do the required task like this.Make sure to import System.Text so you can use the StringBuilderOutput : 00 00 00 00 00 00 11 10
Dim data As String = "0000000000001110"
Dim sb As New StringBuilder()
Dim addSpace As Boolean = False
For Each c As Char In data
If addSpaceThen
sb.Append(c + " ")
addSpace = False
Else
sb.Append(c)
addSpace = True
End If
Next
sb.Length = sb.Length - 1 ''Remove last space on string
Console.WriteLine(sb.ToString())
If you NuGet "System.Interactive" you gt a very neat Buffer operator for IEnumerable(Of T). Then this works:
Dim data As String = "0000000000001110"
Dim result = String.Join(" ", data.Buffer(2).Select(Function (x) New String(x.ToArray())))
If you want to use straight LINQ then this works:
Dim result = String.Join(" ", data.Select(Function (x, n) New With { x, n }).GroupBy(Function (x) x.n \ 2, Function (x) x.x).Select(Function (y) New String(y.ToArray())))
'This is the easiest and a layman level solution
Dim i As Long
Dim A, b, C As String
A = Mid(mac, i + 1, 2) 'assign the first 2 value to the variable
C = A 'transfer it to main section
For i = 0 To Len(mac) - 4 Step 2 ' counting every 2 chars and looping should be 4 characters less.
b = Mid(mac, i + 3, 2) ' assign 2 chars to data
b = "-" + b'put the dashes in front of every other character
C = C + b
Next i
I'm trying to make a molecular composition calculator but i can seem to separate a formula by case and numbers into different cells.
Is it possible to do this in excel?
Eg:
Cl2H0 ----> Cl | 2 | H | 0
A bit crude but you could write a parsing function like this that returns an array:
Public Function parseChem(str As String) As Variant()
'should error-check first that entire string is correct
Dim retArr() As Variant
Dim i As Long, numBlocks As Long
Dim currentChar As String, currentElement As String, typeOfChar As String
Dim digitChain As Boolean
For i = 1 To Len(str)
currentChar = Mid(str, i, 1)
typeOfChar = charType(currentChar)
Select Case typeOfChar
Case Is = "upperCase"
If currentElement <> "" Then
'possibly cast numbers to longs here, and at the end...
retArr(numBlocks) = currentElement
End If
numBlocks = numBlocks + 1
ReDim Preserve retArr(1 To numBlocks)
currentElement = currentChar
digitChain = False
Case Is = "lowerCase"
currentElement = currentElement & currentChar
Case Is = "digit"
If digitChain Then
currentElement = currentElement & currentChar
Else
'new digit block
retArr(numBlocks) = currentElement
numBlocks = numBlocks + 1
ReDim Preserve retArr(1 To numBlocks)
digitChain = True
currentElement = currentChar
End If
Case Else
'do something to flag error
End Select
Next i
retArr(numBlocks) = currentElement
parseChem = retArr
End Function
Private Function charType(str As String) As String
Dim ascii As Long
ascii = Asc(str)
If ascii >= 65 And ascii <= 90 Then
charType = "upperCase"
Exit Function
Else
If ascii >= 97 And ascii <= 122 Then
charType = "lowerCase"
Exit Function
Else
If ascii >= 48 And ascii <= 57 Then
charType = "digit"
Exit Function
End If
End If
End If
End Function
OK the algorithm in the end is very simple
If at any point in the formula you have a number, then look for the next capital letter and output all characters up to that point.
If at any point in the formula you have a letter, then look for the next capital letter *or number* and output all characters up to that point.
The formula is rather long
=IF(ISNUMBER(MID($A$1,SUM(LEN($B$1:B1))+1,1)+0),
MID(MID($A$1,SUM(LEN($B$1:B1))+1,9),1,MIN(FIND( MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ",ROW($1:$26),1),MID($A$1,SUM(LEN($B$1:B1))+2,9)&"ABCDEFGHIJKLMNOPQRSTUVWXYZ" ))),
MID(MID($A$1,SUM(LEN($B$1:B1))+1,9),1,MIN(FIND( MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",ROW($1:$36),1),MID($A$1,SUM(LEN($B$1:B1))+2,9)&"ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" ))))
must be entered as an array formula using CtrlShiftEnter and the 9 would need increasing ( or changing to len($a1) ) if the formula was longer than 9 characters.
Here's a shorter version that doesn't have to be entered as an array formula
=IF(ISNUMBER(MID($A1,SUMPRODUCT(LEN($B1:B1))+1,1)+0),
MID(MID($A1,SUMPRODUCT(LEN($B1:B1))+1,9),1,AGGREGATE(15,6,FIND( MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ",ROW($1:$26),1),MID($A1,SUMPRODUCT(LEN($B1:B1))+2,9)&"A" ),1)),
MID(MID($A1,SUMPRODUCT(LEN($B1:B1))+1,9),1,AGGREGATE(15,6,FIND( MID("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",ROW($1:$36),1),MID($A1,SUMPRODUCT(LEN($B1:B1))+2,9)&"A"),1)))
If you are familiar with VBA then you could write a function which reads in the cell value (e.g. Cl2H0) and then a For Loop that splits the string into seperate values. You would then write these seperated values (Cl, 2, H and 0) back to indivisual columns on the excel sheet.
One way of doing this would be to use the Asc() function in a loop which will give you the Ascii number corresponding to an indivisual charachter. Ascii charachters 65 to 90 are Upper Case charachters. In your case you would want to split the string when the charachter does not fall within this range.
If you want to try this and post your example then I can give some more guidance but its hard to give more advide without first understanding if you are trying to achieve this with VBA or some other means.
I am trying to create a function that will check an array for non-numeric characters. I'm sure there is a way with existing functions, but I was hoping to make a clean function.
This is what I have:
Public Function ContainsAllNumbers(Text() As Variant) As Boolean
For Each element In Text
If Not IsNumeric(element) Then
ContainsAllNumbers = False
End If
Next
End Function
The arguments I want it to take would be something like 11013688 or K03778 or 9005110-4400. I need to know if these strings contain something that is not a number.
Here is a (debugged) function which takes a string input and returns True if and only if all of the characters in the string are digits. This is (perhaps) what you are trying to do:
Function AllDigits(s As String) As Boolean
Dim i As Long
For i = 1 To Len(s)
If Not IsNumeric(Mid(s, i, 1)) Then
AllDigits = False
Exit Function
End If
Next i
AllDigits = True
End Function
I assume you want a function that takes an array of strings and checks them for nun-numeric values. Then your question would be, why your function always returns False.
The default value of a Boolean is false so you need to set ContainsAllNumbers = True at the beginning of the function.
I also recommend using Option Explicit so you don't forget to Dim your variables.
Convert the characters to their ascii code and check each of them. You can Look up an ascii table to find out the specific values for certain characters.
As an example I just copied this from one of my macros. It checks for non-alphanumeric values in the input string str:
invalidCharacter = false
For pos = 1 To Len(str)
Select Case Asc(Mid(str, pos, 1)) 'ascii value of character at str(pos)
Case 48 To 57, 65 To 90, 97 To 122 '0-9,a-z,A-Z
'nothing
Case Else
invalidCharacter = True
End Select
Next pos
If invalidCharacter Then
MsgBox "only numbers or letters may be used"
End If