I'm currently working on project where I am required to take a simple string of characters:
string ("Example")
and convert to an array of hex bytes:
HexByteArray(45 ,78 ,61 ,6d ,70 ,6c ,65)
in VBA I achieved this using strconv:
bytes = StrConv(id, vbFromUnicode)
Struggling to find equivalent functionality in VB, so far I have managed to create an integer equivalent using the Hex() function but as mentioned I need to store each characters hex equivalent within a byte array. Probably a simple solution, all help appreciated!
Sub GetHexString(Value As String)
Dim Bytes() As Byte = Text.Encoding.ASCII.GetBytes(Value)
' StringBuilder Capacity for 2 characters plus space per byte
With New StringBuilder(Bytes.Length * 3)
For Each B As Byte In Bytes
' note the trailing space in the format
.AppendFormat("{0:x2} ", B)
Next
Debug.Print(.ToString.Trim)
' If you want an array of strings, split on the spaces
Dim HexString() As String = Split(.ToString.Trim, " ")
End With
End Sub
Output of GetHexString("Example"):
45 78 61 6d 70 6c 65
Related
I want to split many strings into its ASCII values.
Dim aoC(99), i As Integer
Dim W As String
W="abcd"
For i = 1 To Len(W)
aoC(i) = Asc(Mid(W, i, 1))
Next
Is there a faster method?
You can use
Dim aoC() as Byte
aoC="abcd"
This will give you a byte array of low,high bytes for the 16 bit value for each character in the string. For ascii values the high byte is 0. So to iterate the aoC for the low byte only you would use something like
For myIndex = 0 to ubound(aoC) step 2
Use the locals window to see the raw data.
I need to convert a 2-byte signed integer into a string of it's hex equivalent, but I need the string to be 4 characters. I've tried the Hex() function but when I convert 0 or say, 10, the result is 0 or A. I need the result to be 0000 or 000A. Any tips or advice?
It is just
Dim hexS As String = i.ToString("X4")
This is well explained in the The Hexadecimal (X) Format specifier.
The 4 after the X specifies how many chars you want in the output and if there are not enough chars, the missing ones are supplied as an appropriate number of "0".
Since you also tagged the question VBA, here is a VBA way to do it
Right("0000" & Hex(i), 4)
Use
Dim i As Integer = 10
Dim hexS As String = i.ToString("X4")
If you are deadset on using Hex as opposed to string formatting then you could use:
Dim Number As Integer
Dim Output As String
Number = 10
Output = ("000" & Hex(Number))
Output = Output.Substring(Output.Length - 4, 4)
Console.WriteLine(Output)
Alternatively make use of string formatting for numbers as so:
Output = Number.ToString("X4")
Console.WriteLine(Output)
The output in both cases with be 000A.
The VB way
Format(i, "X4")
or
Hex(i).PadLeft(4, "0"c)
In Visual Studio 2015:
s = $"{i:X4}"
In my application, the users may need to add hexadecimals in the textbox during run time and I would like to get each hexadecimal value in an array by removing the spaces if there are any.
For example, the data in the textbox is "ff 00 ff 1a ff 00". I could able to get each hexadecimal as an array with the help of split. But, now I am trying to remove all the spaces as some users may enter the hexadecimals without giving the space. For example, the user my enter as "ff 00 ff1a ff 00". So, in this case the third array contains "ff1a", which is not true. I know how to remove the spaces, but cannot able to figure out the way to get each hexadecimal into an array.
'Used to split the strings based on the spaces, which is not useful
'Dim extraData As String() = Split(TextBox8.Text, " ")
'Used to remove the spaces
Dim myString = TextBox8.Text.Replace(" ", "")
Any suggestion will be appreciated.
You can use something like:
Dim input As String = TextBox1.Text
Dim list As New List(Of String)
input = input.Replace(" ", "")
For i As Integer = 0 To input.Length - 1 Step 2
list.Add(input.Substring(i, 2))
Next
Dim array = list.ToArray()
Note - The length of the input (after removing spaces) must be even. Otherwise, it may throw an exception.
If I convert the method from C# to VB.NET given in this answer:
Public Function StringToByteArray(hex As [String]) As Byte()
Dim NumberChars As Integer = hex.Length
Dim bytes As Byte() = New Byte(NumberChars / 2 - 1) {}
For i As Integer = 0 To NumberChars - 1 Step 2
bytes(i / 2) = Convert.ToByte(hex.Substring(i, 2), 16)
Next
Return bytes
End Function
Then you can call it like this:
StringToByteArray(YourInputString.Replace(" ", ""))
This will return an array of bytes and will also validate the input because it will thrown an exception if invalid characters are used in the input (like non-hex characters).
I know you already have accepted an answer, however, I'd like to suggest another alternative. I'm assuming, based on the context of your question and the example that you provided, that your intention is to parse a list of bytes, where each byte is represented by a two digit hexadecimal value.
If you use regex to find your matches, it will be more powerful. For instance, you can easily ignore all white-space characters (e.g. tabs, new-lines) and you can easily validate the data to make sure that only properly formatted bytes are in the string (i.e. 00 - FF). For instance, you could do something like this:
Dim bytes As New List(Of String)()
Dim m As Match = Regex.Match(TextBox1.Text, "^\s*((?<byte>[a-fA-F0-9]{2})\s*)*$")
If m.Success Then
Dim g As Group = m.Groups("byte")
If g.Success Then
For Each c As Capture In g.Captures
bytes.Add(c.Value)
Next
End If
Else
MessageBox.Show("Input is not properly formatted")
End If
However, if the idea is to parse the actual byte values, you can do so using Byte.Parse, like this:
Dim bytes As New List(Of Byte)()
Dim m As Match = Regex.Match(TextBox1.Text, "^\s*((?<byte>[a-fA-F0-9]{2})\s*)*$")
If m.Success Then
Dim g As Group = m.Groups("byte")
If g.Success Then
For Each c As Capture In g.Captures
bytes.Add(Byte.Parse(c.Value, NumberStyles.HexNumber))
Next
End If
Else
MessageBox.Show("Input is not properly formatted")
End If
Both of those examples use the same regex pattern to find all of the hexadecimal bytes in the string. The pattern it uses is:
^\s*((?<byte>[a-fA-F0-9]{2})\s*)*$
Here's the meaning of the regex pattern:
$ - The match must start at the beginning of the string (i.e. no invalid text can come before the bytes)
\s* - Any number of white-space characters can come before the first byte
\s - Any white-space character
* - Zero or more times
((?<byte>[a-fA-F0-9]{2})\s*)* - Any number of bytes, each separated by any number of white-space characters
( - Begins the group so that we can later apply a kleene star to the whole group (which means the whole group can repeat any number of times)
(?<byte> - Begins another, inner group which is named "byte". It is given a name so that we can easily reference the values captured by the group in the VB code.
[a-fA-F0-9]{2} - A two-digit hexadecimal byte (i.e. 00 - FF)
[a-fA-F0-9] - Any character between a and f or between A and F or between 0 and 9
{2} - There must be exactly two characters matching that specification
) - Ends the group named "byte". Notice that this named-group only captures the two actual hexadecimal digits. It does not capture any of the white-space between the bytes.
\s* - There can be any number of white-space characters after a byte
) - The ends outer group which includes a byte and all of the white-space that comes after it
* - The aforementioned kleene star meaning that the outer-group can repeat any number of times
$ - The match must end at the ending of the string (i.e. no invalid text can come after the bytes)
I have a calculation that returns a decimal with a maximum of two decimal places. What I'd like to achieve is a result that doesn't include decimal zeros, so 8.00 is shown as 8, 8.10 is shown as 8.1 and 8.12 is shown as 8.12.
I've been all over the math functions and can't find anything to achieve this - can anyone point me in the right direction?
As Benjamin Leinweber pointed out in one of the comments it's probably a product of it being displayed as a string. That said, you could take the duct tape approach and just lop off the trailing digits you don't want like this (who doesn't love duct tape every so often):
' Hacky, I did this because Visual Studio removes the 0 in the editor
Dim num As Decimal = CDec("8.10")
' This will now output 8.10
Console.WriteLine(num)
' Put it in a string then trim off the 0's and then the decimal place if it then happens to be at the end
Dim buf As String = num.ToString
buf = buf.TrimEnd("0")
buf = buf.TrimEnd(".")
' This will output 8.1
Console.WriteLine(buf)
Are you talking about the VB Decimal data type? If so then read this from the MSDN documentation...
Trailing Zeros. Visual Basic does not store trailing zeros in a Decimal literal.
However, a Decimal variable preserves any trailing zeros acquired computationally.
The following example illustrates this.
Dim d1, d2, d3, d4 As Decimal
d1 = 2.375D
d2 = 1.625D
d3 = d1 + d2
d4 = 4.000D
MsgBox("d1 = " & CStr(d1) & ", d2 = " & CStr(d2) &
", d3 = " & CStr(d3) & ", d4 = " & CStr(d4))
The output of MsgBox in the preceding example is as follows:
d1 = 2.375, d2 = 1.625, d3 = 4.000, d4 = 4
So since the Decimal data type holds on to significant digits in a calculation then you may want to convert the data type to double and use a custom format like {0:#.##} for display
MSDN Decimal Data Type
.ToString("###") or however many octothorpes for however many to the left of the decimal.
If you want to have... say, 2 decimal places:
.ToString("###.##")
You can do it by using TrimEnd function :
Dim number1 As Double = 8.0
Dim number2 As Double = 8.1
Dim number3 As Double = 8.12
Console.WriteLine(number1.ToString("N2").TrimEnd("0"c).TrimEnd("."c))
Console.WriteLine(number2.ToString("N2").TrimEnd("0"c).TrimEnd("."c))
Console.WriteLine(number3.ToString("N2").TrimEnd("0"c).TrimEnd("."c))
Consider that formatting with "F0" will remove 1000 separators too.
From what you are describing, it sounds like you are using a Fixed-point format when you should be using a Number format or a General format.
This sample code taken from the MSDN shows how to use the Number format:
Dim dblValue As Double = -12445.6789
Console.WriteLine(dblValue.ToString("N", CultureInfo.InvariantCulture))
' Displays -12,445.68
Console.WriteLine(dblValue.ToString("N1", _
CultureInfo.CreateSpecificCulture("sv-SE")))
' Displays -12 445,7
Dim intValue As Integer = 123456789
Console.WriteLine(intValue.ToString("N1", CultureInfo.InvariantCulture))
' Displays 123,456,789.0
For a complete list of formatters, have a look at this MSDN article.
This solution applies to different cultures and works with integer numbers ends with Zero
`
Public Module DecimalExt
<Extension()>
Function ToStringWithoutZeros(ByRef value As Decimal) As String
Dim buf As String = value.ToString("n10")
buf = buf.TrimEnd("0").TrimEnd(",").TrimEnd(".")
Return buf
End Function
End Module
`
I think You can try with Val Function
Like we we have a Decimal Value like
8.00
8.01
8.590
Then after using the Val() function it will convert this decimals to
Dim number as Decimal = 8.00
`Msgbox(Val(number))` // will gives you 8
What is the best appraoch to generate unquie ID's (no special characters) that will be 25 characters in length? I was thinking of generating a GUID and taking a substring of that, but I dont know if thats the best idea for uniqueness.
This is for dissconnected systems use. Creating a primary key in a database will not work in my situation. I need to create a unquie ID manually
I tried this but I am seeing duplicates in the output for some reason. So it doesnt seem too unquie even in this simple test..
Sub Main()
Dim sb As StringBuilder = New StringBuilder
For i As Integer = 1 To 100000
Dim s As String = GenerateRandomString(25, True)
sb.AppendLine(s)
sb.AppendLine(Environment.NewLine)
Next
Console.WriteLine(sb.ToString)
Console.ReadLine()
End Sub
Public Function GenerateRandomString(ByRef len As Integer, ByRef upper As Boolean) As String
Dim rand As New Random()
Dim allowableChars() As Char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLOMNOPQRSTUVWXYZ0123456789".ToCharArray()
Dim final As String = String.Empty
For i As Integer = 0 To len - 1
final += allowableChars(rand.Next(allowableChars.Length - 1))
Next
Return IIf(upper, final.ToUpper(), final)
End Function
You’re probably seeing duplicates because New Random() is seeded according to a system clock, which may not have changed by the next iteration.
Try a cryptographically secure RNG:
Const ALLOWABLE_ALL As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Const ALLOWABLE_UPPERCASE As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Dim allowable As String = If(upper, ALLOWABLE_UPPERCASE, ALLOWABLE_ALL)
Dim result(len - 1) As Char
Dim current As Integer = 0
Using r As New Security.Cryptography.RNGCryptoServiceProvider()
Do
Dim buffer(255) As Byte
r.GetBytes(buffer)
For b As Byte In buffer
If b < allowable.Length Then
result(current) = allowable(b)
current += 1
If current = len Then Return New String(result)
End If
Next
Loop
End Using
This is also “more random” than your implementation in that letters aren’t weighted twice as heavily if upper is True.
A GUID might be 32 digits, but only if expressed in hexadecimal. That means it will only use characters 0-9 and A-F. If your string can use the entire alphabet then you can express the same GUID in fewer characters, especially if your string can be case sensitive.
See http://en.wikipedia.org/wiki/Globally_unique_identifier#Text_encoding for an example of alternative encoding, or http://web.archive.org/web/20100408172352/http://prettycode.org/2009/11/12/short-guid/ for example code. EDIT: Or Hans's method above which is much better. If you want to encode a GUID with only A-Z, a-z and 0-9 characters then you will need to look up Base-62 encoding (as opposed to base-64) because you only have 62 characters to encode into.
Stop trying to re-invent the wheel and just use .NET's built in GUID generator:
System.Guid.NewGuid()
which will generate a properly randomly seeded GUID, then simply sub-string it to your limit. Even better if you grab the last 25 chars, instead of the first 25.
PS: I don't consider this a great idea in general, because it's the entire GUID that's considered unique, not part of it, but it should satisfy what you want.