Convert bitarray to string without using ChrW function - string

I need to convert bitarray to unicode string (default encoding in vb.net for string type).
For example, let's say the 000000000100000100000000010000100000000001000011 is the bit string representation for 3 x 16bit unicode characters, respectively A = 65; B = 66 and C = 67 (codePoints resulted in conversion of those bits to Integer).
Now, those bits are stored in bitarray. Is there a way to convert the bitarray to string without using the build-in ChrW function?
I need this because since I already have the bits ordered as encoding would do, so I try to avoid a double conversion to gain some performance.

Use Byte() and System.Text.Encoding...
'BIN: 00000000 01000001 00000000 01000010 00000000 01000011
'HEX: 0 0 4 1 0 0 4 2 0 0 4 3
Dim s1 As String = "ABC"
Console.WriteLine("Original string:" & s1)
Dim b1 As Byte() = System.Text.Encoding.BigEndianUnicode.GetBytes(s1)
Console.WriteLine("Big-endian UTF-16 (Hex):" & BitConverter.ToString(b1))
b1(1) = b1(1) Or CByte(&H4) 'change byte 1 from 01000001 to 01000101
Dim s2 As String = System.Text.Encoding.BigEndianUnicode.GetString(b1)
Console.WriteLine("Modified string:" & s2)
Console.ReadKey()

I don't know if this'll actually be faster than using ChrW(), and it's probably not the prettiest solution either ;), but I here's how I did it:
To do what you ask I did these steps:
Reverse the bit array (since .NET interprets them from left to right, rather than right to left)
Create a byte array of the bits. The size of the array should be the amount of bits divided by 8 (8 bits = 1 byte) rounded up to the nearest integer (see Math.Ceiling()).
Use Encoding.Unicode to decode the byte array into a string.
Convert the string into an array of chars, reverse it, and convert the new char array back into a string.
I've put this together in a function:
Public Function BitArrayToString(ByVal Bits As BitArray) As String
'Reverse the bits (I didn't have access to a compiler tha supports Linq, please don't hate).
Dim ReversedValues As Boolean() = New Boolean(Bits.Count - 1) {}
For x As Integer = 0 To Bits.Count - 1
ReversedValues((ReversedValues.Length - 1) - x) = Bits(x)
Next
'Put the reversed bits into a new bit array.
Dim ReversedBits As New BitArray(ReversedValues)
'Declare a byte array to 1/8th of the bit array's size, rounded up to the nearest integer.
Dim Bytes As Byte() = New Byte(Math.Ceiling(ReversedBits.Length / 8) - 1) {}
ReversedBits.CopyTo(Bytes, 0)
'Decode the byte array into a string.
Dim Result As String = System.Text.Encoding.Unicode.GetString(Bytes)
'Get the string as a char array and reverse it.
Dim Chars As Char() = Result.ToCharArray()
Array.Reverse(Chars)
'Return the resulting string from our reversed chars.
Return New String(Chars)
End Function
Online test: https://ideone.com/SUTWlJ

Related

Excel VBA write output file with mixture of ASCII strings and low-value bytes (all values set to zero) -

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

Recursive function to convert from decimal to binary

My code has a problem with conversion of the number 3
I would like to write a function which converts a decimal number into a binary one. The principle of recursion must be used. I have already written the following code.
Function recursive(number As Integer) As String
Dim result As String
If number > 0 Then
Dim binaryNumber As String
Dim digit As Integer
binaryNumber = recursive(number / 2)
digit = number Mod 2
result = result & binaryNumber & digit
End If
recursive = result
End Function
Right result:
Input: 10
Output: 1010
Wrong result:
Input: 3
Output: 101
It also works reasonably well, but I get a wrong result when I try to convert the decimal number 3. Where is the error?
Function recursive(number As Integer) As String
Dim result As String
If number > 0 Then
Dim binaryNumber As String
Dim digit As Integer
digit = number Mod 2
number = Int(number / 2)
binaryNumber = recursive(number)
result = result & binaryNumber & digit
End If
recursive = result
End Function

Replace a string with a format string

I have the following input:
Dim str_format as string = "XXXXX00000"
Dim str as string = "INV"
Dim int as integer = "56"
How can I replace XXXXX with INV and replace 00000 with 56?
For the example above the result should be INVXX00056.
X can only replace with alphabet and 0 can only replace with integer, if str has more than five alphabet. The extra alphabets will be thrown away because str_format only has five X. The same algorithm is true for the integer.
Example 2
Dim str_format as string = "XXX00000"
Dim str as string = "ABCD"
Dim int as integer = 654321
Expected result: ABC54321
Process:
1. ABCD XXX00000 654321
2. ABC DXX000006 54321
3. AB CDX00065 4321
4. A BCD00654 321
5. ABC06543 21
6. ABC65432 1
7. ABC54321
As Spidey mentioned... show some code. That said the process you describe is a bit long-winded.
The Letter part of the solution can be done by grabbing the first 3 characters of str using Left(str,3) this will bring in the leftmost 3 character (if there are less it will get what is there). Then check that you have 3 characters using str.Length(). If the length is less than 3 then append the appropriate number of 'X'.
The Numeric part can be done in a similar way. Your int is actually a string in your code above. If it was a real integer you can cast it to string. Use Right(int,5). Again check to see you have 5 digits and if not prepend with appropriate number of 0.
Have a go... if you run into problems post your code and someone is bound to help.
UPDATE
As there have been actual answers posted here is my solution
Function FormatMyString(str As String, num as String) As String
Dim result As String
result = Left(str,3).PadRight(3, "X"c).ToUpper() & Right(num,5).PadLeft(5, "0"c)
Return result
End Function
UPDATE 2
based on Wiktors answer... made an amendment to my solution to cope with different formats
Function FormatMyString(str As String, num as String, alpha as Integer, digits as Integer) As String
Dim result As String
result = Left(str, alpha).PadRight(alpha, "X"c).ToUpper() & Right(num, digits).PadLeft(digits, "0"c)
Return result
End Function
To use...
FormatMyString("ABCDE", "56",3 5) will return ABC00056
FormatMyString("ABCDE", "123456",4 3) will return ABCD456
FormatMyString("AB", "123456",4 3) will return ABXX456
Here is a possible solution that just uses basic string methods and PadLeft/PadRight and a specific method to count occurrences of specific chars in the string. It assumes the format string can only contain X and 0 in the known order.
Public Function CountCharacter(ByVal value As String, ByVal ch As Char) As Integer
Return value.Count(Function(c As Char) c = ch)
End Function
Public Sub run1()
Dim str_format As String = "XXXXX00000" '"XXX00000"
Dim str As String = "INV"
Dim int As Integer = 56 ' ABC54321
Dim xCnt As Integer = CountCharacter(str_format, "X")
Dim zCnt As Integer = CountCharacter(str_format, "0")
Dim result As String
If xCnt > str.Length Then
result = str.PadRight(xCnt, "X")
Else
result = str.Substring(0, xCnt)
End If
If zCnt > int.ToString().Length Then
result = result & int.ToString().PadLeft(zCnt, "0")
Else
result = result & int.ToString().Substring(int.ToString().Length-zCnt
End If
Console.WriteLine(result)
End Sub
Output for your both scenarios is as expected.
Take a look at this sample
Dim str_format As String = str_format.Replace("XXX", "ABC")
Msgbox(str_format )
As we assume that the X is 3 only. I dont want to give you more it is a start and everything will be easy.
If that kind of format is fix I mean the number of X will go or down then you can make a conditional statement based on the length of string

How do I perform a XOR calculation of two binary numbers in excel 2007

I wanted to perform a XOR calculation of two Binary numbers for example: on Sheet 1
Range A1 = 10101010
Range A2 = 11100010
Now I need to perform XOR of A1, A2 result in A3. I tried different formula's two perform XOR calculations like: A1^A2, (BITXOR (A1, A2)) but unfortunately it didn't worked I think because I am using excel 2007 "XOR" doesn't support.
I'm expecting a result of 1001000.
First, you should note that Excel pre-Excel2013 has no bitwise operators or functions built-in (Excel's OR() function is logical even if the operands are numeric). Excel 2013 finally adds this glaringly missing functionality.
Using VBA
The simplest way is to create a User Defined Function that does it. Formulae can work if you are prepared for either a decimal output, or helper columns, or a very repetitive Concatenate formula but VBA gets around these limitations - I recommend it if you are able to have code in the workbook.
Decimal Input, Decimal Output
The below examples just expose the built-in bitwise operators to use as functions in Excel formulae. I assume an integral type, although you could change it to accept decimals etc.
You can convert your string binary numbers (e.g. "1010") to decimals (10, for the previous example) using the BIN2DEC() function built-in to Excel, although this only handles 9 bits + sign bit, alternatively you can use an array formula to convert it for you (see my section on "Using Formulas" below).
Public Function BITWISE_OR(operand1, operand2)
BITWISE_OR = CLng(operand1) Or CLng(operand2)
End Function
Public Function BITWISE_AND(operand1, operand2)
BITWISE_AND = CLng(operand1) And CLng(operand2)
End Function
Public Function BITWISE_XOR(operand1, operand2)
BITWISE_XOR = CLng(operand1) Xor CLng(operand2)
End Function
Converting the numeric results back to binary strings is pretty annoying with formulas - if you need more than the range covered by DEC2BIN() (a paltry -512 to +511) function built in to Excel then I would suggest either using VBA (see below), or building up your binary string bit by bit using columns or rows (see my Using Formulas section below).
Binary string input, Binary string output
The below essentially iterates through a string setting each bit in turn based on the corresponding bits in the input strings. It performs the bit changes on the string in-place using Mid$ statement. Bit strings can be arbitrary length.
The below looks complicated but really it is the same basic stuff repeated 3 times for each of And, Or and XOr.
'str1, str2: the two bit strings. They can be different lengths.
'significantDigitsAreLeft: optional parameter to dictate how different length strings should be padded. Default = True.
Public Function Bitstr_AND(str1 As String, str2 As String, Optional significantDigitsAreLeft As Boolean = True)
Dim maxLen As Long, resStr As String, i As Long
If Len(str1) > Len(str2) Then maxLen = Len(str1) Else maxLen = Len(str2) 'get max length of the two strings
str1 = getPaddedString(str1, maxLen, significantDigitsAreLeft) 'pad left or right to the desired length
str2 = getPaddedString(str2, maxLen, significantDigitsAreLeft) 'pad left or right to the desired length
resStr = String$(maxLen, "0") 'prepare the result string into memory (Mid$ can operate without creating a new string, for performance)
For i = 1 To maxLen
If Mid$(str1, i, 1) = "1" And Mid$(str2, i, 1) = "1" Then
Mid$(resStr, i, 1) = "1" 'in-place overwrite of the existing "0" with "1"
End If
Next i
Bitstr_AND = resStr
End Function
'For explanatory comments, see Bitstr_AND
Public Function Bitstr_OR(str1 As String, str2 As String, Optional significantDigitsAreLeft As Boolean = True)
Dim maxLen As Long
Dim resStr As String
Dim i As Long
If Len(str1) > Len(str2) Then maxLen = Len(str1) Else maxLen = Len(str2)
str1 = getPaddedString(str1, maxLen, significantDigitsAreLeft)
str2 = getPaddedString(str2, maxLen, significantDigitsAreLeft)
resStr = String$(maxLen, "0")
For i = 1 To maxLen
If Mid$(str1, i, 1) = "1" Or Mid$(str2, i, 1) = "1" Then
Mid$(resStr, i, 1) = "1"
End If
Next i
Bitstr_OR = resStr
End Function
'For explanatory comments, see Bitstr_AND
Public Function Bitstr_XOR(str1 As String, str2 As String, Optional significantDigitsAreLeft As Boolean = True)
Dim maxLen As Long
Dim resStr As String
Dim i As Long
If Len(str1) > Len(str2) Then maxLen = Len(str1) Else maxLen = Len(str2)
str1 = getPaddedString(str1, maxLen, significantDigitsAreLeft)
str2 = getPaddedString(str2, maxLen, significantDigitsAreLeft)
resStr = String$(maxLen, "0")
For i = 1 To maxLen
If Mid$(str1, i, 1) = "1" Then
If Not Mid$(str2, i, 1) = "1" Then
Mid$(resStr, i, 1) = "1"
End If
ElseIf Mid$(str2, i, 1) = "1" Then 'Save an If check by assuming input string contains only "0" or "1"
Mid$(resStr, i, 1) = "1"
End If
Next i
Bitstr_XOR = resStr
End Function
'Helper to pad string
Private Function getPaddedString(str As String, length As Long, padLeft As Boolean) As String
If Len(str) < length Then
If padLeft Then
getPaddedString = String$(length - Len(str), "0") & str
Else
getPaddedString = str & String$(length - Len(str), "0")
End If
Else
getPaddedString = str
End If
End Function
Using Formulas
You can do an XOR operation using Text functions or Sumproduct. This may be more appropriate if you do not want to use VBA but formulas are painful to ensure they covers all situations, like negatives or different length binary strings. I refer you to the superb blog post http://www.excelhero.com/blog/2010/01/5-and-3-is-1.html for examples using Sumproduct, and http://chandoo.org/wp/2011/07/29/bitwise-operations-in-excel/ for examples using Text functions.
I cooked up my own formulae that handles certain cases and I explain them below to guide you.
Binary string Input, Decimal Output
In the below, A2 and B2 refer to the two binary numbers in up to 32-bits string form. The strings can be variable length, as the formula will pad with 0's to the necessary length. It should be obvious how to increase it to more bits. They must be entered using Ctrl+Shift+Enter.
The most significant bit is on the left. To make it least significant bit on the left, you can remove the little subtraction in the powers of 2 part, and make it pad to the right.
Bitwise And:
=SUM((((MID(REPT("0",32-LEN($A$2))&$A$2,ROW($1:$32),1)="1")+(MID(REPT("0",32-LEN($B$2))&$B$2,ROW($1:$32),1)="1"))=2)*(2^(32-ROW($1:$32))))
Bitwise Or:
=SUM((((MID(REPT("0",32-LEN($A$2))&$A$2,ROW($1:$32),1)="1")+(MID(REPT("0",32-LEN($B$2))&$B$2,ROW($1:$32),1)="1"))>0)*(2^(32-ROW($1:$32))))
Bitwise Xor:
=SUM((((MID(REPT("0",32-LEN($A$2))&$A$2,ROW($1:$32),1)="1")+(MID(REPT("0",32-LEN($B$2))&$B$2,ROW($1:$32),1)="1"))=1)*(2^(32-ROW($1:$32))))
Binary string input, Binary string Output
A single cell solution would be arduous because there is no array concatenation formula in Excel. You could do it using the CONCATENATE function glueing together each bits, with each bit being the result of an If comparing each binary string returning 1 or 0 as appropriate. As I said, though easy (just build it up like =IF(Mid(A1,1,1) = "1",...), this would be boring so I personally won't do it for you ;)
Alternatively, you could do it more simply using columns or rows to build up the string, like:
If A1 and B1 have your binary strings, then in C1 put (for AND, or for OR change the =2 at the end to >0 and for XOR change it to =1):
=IF((MID($A1,1,1)="1")+(MID($B1,1,1)="1"))=2,"1","0")
Then in D1 put:
=C1 & IF((MID($A1,COLUMN()-COLUMN($C1),1)="1")+(MID($B1,COLUMN()-COLUMN($C1),1)="1"))=2,"1","0")
Then drag this across as many columns as bits

Dividing a string in groups of two

I have a Hex string that has a value like
26C726F026C426A1269A26AB26F026CC26E226C726E226CD
I was wondering how to split it into a string array, where each index of the array holds a group of 2 of those chars.
Example:
string(0)=26,string(1)=C7,string(2) = 26,string (3) = F0, and so on.
How can I do this?
Dim MyList as New List(Of String)
Dim s as String = "26C726F026C426A1269A26AB26F026CC26E226C726E226CD"
For x as Integer = 0 to s.Length - 1 step 2
MyList.Add(s.substring(x,2))
Next
You can get it with MyList(0), MyList(1) or etc

Resources