Mods and ASCII in VB Caesar Shift - vb.net

I have this code which shifts the alphabet by a certain amount. The size of the alphabet is 26. When I enter a larger size shift (for example 22) I get some weird characters displaying. I think I need to mod the ASCII alphabet to 26 to get it working but Im not quite sure which bit to mod.
Basically I need to wrap around the alphabet (once it reaches Z it goes back to letter A) Do I have to create a dictionary for the mod to work (like A = 0... Z = 26) or can I stick with using the normal ASCII table? Here is the code below:
Public Function encrypt(ByVal input As String) 'input is a variable within the funcion
Dim n as Integer
Dim i As Integer
n = key.Text Mod 26 'gets what is in the text box of 'key' and sets it as n
' the key is a multiple of 26 so 26 will = 0
'need to remove white spaces
While input.Contains(" ") 'when the input text contains a space
input = input.Replace(" ", "") 'replaces it with no space.
End While
For i = 1 To Len(input) 'find the length of the input
Mid(input, i, 1) = Chr(Asc(Mid(input, i, 1)) + n) 'chr returns the character associated with the specified character code
'
Next
encrypt = input
End Function

Look at this code:
For i = 1 To Len(input) 'find the length of the input
Mid(input, i, 1) = Chr(Asc(Mid(input, i, 1)) + n) 'chr returns the character associated with the specified character code
'
Next
String indexes are 0-based. Your first index is 0, not 1! Also, you are assigning to the result of a function call. You need to instead construct a new string.
You didn't say, but the way you used the Replace and Contains methods indicates .Net, and if that's the case, I would do it like this:
Public Function encrypt(ByVal key As Integer, ByVal input As String) As String 'Don't forget the return type on the function
key = key Mod 26
Return New String(input.Replace(" ", "").ToUpper().Select(Function(c) Chr(((Asc(c) + key - Asc("A"c)) Mod 26) + Asc("A"c))).ToArray())
End Function
Just like that, and it's almost a one-liner. I can see this works now by calling it this way:
Encrypt("C"c, "the quick brown fox jumps over the lazy dog")
Encrypt("D"c, "the quick brown fox jumps over the lazy dog")
The results:
BPMYCQKSJZWEVNWFRCUXMLWDMZBPMTIHGLWOA
CQNZDRLTKAXFWOXGSDVYNMXENACQNUJIHMXPB
Look for the results mapped for the word "lazy", and you will see that the 'a' wraps to 'z' and 'y' correctly, and that the 'D' key results are one letter off of the 'C' results.

Related

Word input from one line in the textbox to another

I have 5 text boxes marked with NX1, NX2, NX3 .... NX5.
I have a Textbox marked with Textbox2 that contains lines like:
2 4 6 8 11
1 2 3 4 12
3 4 7 9 13
4 5 7 9 14
Is there a possibility to enter the first word / number from TextBox2 Lines (0) in NX1? i.e. First Word (number 2 in NX1), (Second Word) number 4 in NX2, number 6 in NX3 and number 8 in NX4. and so on.
I tried this:
Dim mytext As String
Dim counter As Integer
mytext = TextBox1.Text
For counter = 1 To Len(TextBox1.Text)
If Mid$(mytext, counter, 1) = " " Then
GoTo NextPart
End If
Next counter
NextPart:
MsgBox("'" + Mid$(mytext, 1, counter - 1) + "'")
MsgBox("'" + Mid$(mytext, 2, counter - 1) + "'")
Get the TextBox.Lines(0). That's the first line of text. If the text parts are separated by a white space, just Split() the text (white space is the default separator).
If it's not, specify the separator: Split("[SomeChar]"c).
You'll get 5 strings in a String() array. Assing String(0) to NX1 etc.
Dim FirstLine As String = TextBox2.Lines(0)
Dim AllParts As String() = FirstLine.Split()
NX1.Text = AllParts(0)
NX2.Text = AllParts(1)
'(...)
Repeat the same procedure if you need the other text lines.
You could also use LINQ to perform the string assignments:
AllParts.Select(Function(s, i) Me.Controls("NX" & (i + 1)).Text = s).ToList()
Or, assemble everything in one expression:
TextBox2.Lines(0).Split().Select(Function(s, i) Me.Controls("NX" & (i + 1)).Text = s).ToList()
A description of this method:
[TextBox].Lines(0) => Returns the first line of text in a TextBoxBase control
Split() => Splits the text content using the default separator.
Returns an array of String
Select((string, int)) => Selects each string returned by the Split() method and updates
the index of each iteration.
Performs an action: sets a control`s Text property.
ToList() => Materializes the Enumerable collection returned by Select()
If omitted, in this case, the iteration is performed just once
Me.Controls("NX" & (i + 1)).Text = s:
Returns a Form's Control which name is "NX" + a number and assigns a string to its Text property.
You can use String.Split() to split the string.
Dim columns = TextBox2.Lines(0).Split()
For i = 0 To columns.Length - 1
Controls.Item("NX" & (i + 1)).Text = columns(i)
Next
The Lines property of the TextBox returns a string array containing the lines of the entered text.
String.Split() with no parameters splits a string at white spaces and returns a string array containing the parts (the columns of the first lines in this case).
The Controls property of the form returns a controls collection. The indexed Item property of the controls collection accepts either an Integer index or a control name as String.

Random number creating

I got some help from one, and the code works perfectly fine.
What im looking for, is an explanation of the code, since my basic VBA-knowledge does not provide me with it.
Can someone explain what happens from "Function" and down?
Sub Opgave8()
For i = 2 To 18288
If Left(Worksheets("arab").Cells(i, 12), 6) = "262015" Then
Worksheets("arab").Cells(i, 3) = "18" & UniqueRandDigits(5)
End If
Next i
End Sub
Function UniqueRandDigits(x As Long) As String
Dim i As Long
Dim n As Integer
Dim s As String
Do
n = Int(Rnd() * 10)
If InStr(s, n) = 0 Then
s = s & n
i = i + 1
End If
Loop Until i = x + 1
UniqueRandDigits = s
End Function
n = Int(Rnd()*10) returns a value between 0 and 9, since Rnd returns a value between 0 and 1 and Int converts it to an integer, aka, a natural number.
Then If InStr(s, n) = 0 checks if the random number is already in your result: if not, it adds it using the string concatenation operator &.
This process loops (do) until i = x + 1 where x is your input argument, so you get a string of length x. Then the first part just fills rows with these random strings.
N.B. : I explained using the logical order of the code. Your friend function UniqRandDigits is defined after the "business logic", but it's the root of the code.
The code loops from row 2 to 18288 in Worksheet "arab". If first 6 characters in 12th column are "262015", then in 3rd column macro will fill cell with value "18" followed by result of function UniqueRandDigits(5) which generates 5 unique digits (0-9).
About the UniqueRandDigits function, the most important is that Rnd() returns a value lesser than 1 but greater than or equal to zero.
Int returns integer value, so Int(Rnd() * 10) will generate a random integer number from 0 to 9.
If InStr(s, n) = 0 Then makes sure than generated integer value doesn't exist in already generated digits of this number, because as the function name says, they must be unique.

vba excel - Find and replace on condition + within same cell multiple occurance

I am trying to write a VBA code ; I have 3-days experience as vba programmer. So trying my best based on my pascal programming experience.
find number in hexadecimal string from excel, check the position of number if its odd then replace the number with new number. If its not odd then continue searching for other occurrence within the same string.
I have 15,000 hexa strings where I need to recursively search. range(B1:B15000)
Example:
Hexa string - Cell B1 - 53706167686574746920616c6c9261676c696f2c206f6c696f20652070657065726f63696e692537
translates to text - Spaghetti all�aglio, olio e peperocini
i want to replace 92(�) with 65(e) but in hexa string you notice there are multiple occurrences of 92 number but only one 92 falls at odd position to be replaced.
In excel I tried following:
=IF(ISODD(IF(ISERROR(SEARCH(92,B5)),0,SEARCH(92,B5)))=TRUE,SUBSTITUTE(B5,92,"27"),"no 92")
This works only for first occurrence in cell,
tried modifying it to search further but no luck:
=IF(ISODD(IF(ISERROR(SEARCH(92,B6)),0,SEARCH(92,B6)))=TRUE,SUBSTITUTE(B6,92,"27"),IF(ISODD(IF(ISERROR(SEARCH(92,B6,SEARCH(92,B6)+1)),0,SEARCH(92,B6,SEARCH(92,B6)+1)))=TRUE,SUBSTITUTE(B6,92,"27"),"no 92"))
Any suggestions are welcome.
How about a small UDF, looking only at every second position?
Function replaceWhenAtOddPos(ByVal s As String, findStr As String, replaceStr As String)
replaceWhenAtOddPos = s
If Len(findStr) <> 2 Or Len(replaceStr) <> 2 Then Exit Function
Dim i As Long
For i = 1 To Len(s) Step 2
If Mid(s, i, 2) = findStr Then s = Left(s, i - 1) & replaceStr & Mid(s, i + 2)
Next i
replaceWhenAtOddPos = s
End function
call:
replaceWhenAtOddPos("53706167686574746920616c6c9261676c696f2c206f6c696f20652070657065726f63696e692537", "92", "65")
Please put the following UDF in a standard module:
Public Function replace_hex(str As String, srch As Integer, repl As Integer) As String
Dim pos As Integer
pos = InStr(pos + 1, str, CStr(srch))
Do Until pos = 0
If pos Mod 2 = 0 Then str = Left(str, pos - 1) & CStr(repl) & Mid(str, pos + 2)
pos = InStr(pos + 1, str, CStr(srch))
Loop
replace_hex = str
End Function
and call it in your worksheet like that:
=replace_hex(A1,92,65)

Shift letters to the end of a string Visual Basic

I'm trying to shift letters to the end of the word. Like the sample output I have in the image.
Using getchar and remove function, I was able to shift 1 letter.
mychar = GetChar(word, 1) 'Get the first character
word = word.Remove(0, 1) 'Remove the first character
input.Text = mychar
word = word & mychar
output.Text = word
This is my code for shifting 1 letter.
I.E. for the word 'Star Wars', it currently shifts 1 letter, and says 'tar WarsS'
How can I make this move 3 characters to the end? Like in the sample image.
intNumChars = input.text
output.text = mid(word,4,len(word)) & left(word,3)
I wanted it to be easy for you to read but you can set the intNumChars variable to the value in your text box and replace the 4 with intNumChars + 1 and the 3 with intNumChars.
The mid() function can return a section of text in the middle of a string mid(string,start,finish). The len() function returns the length of a string so that the code will work on texts that are different lengths. The left function returns characters from the left() of a string.
I hope this is of some help.
You could write a that as a sort of permute, which maps each char-index to a new place in the range [0, textLength[
In order to do that you'll have to write a custom modulus as the Mod operator is more a remainder than a modulus (from a mathematical point of view, regarding how negative are handled)
With that you just need to loop over your string indexes and map each one to it's "offsetted" value modulo the length of the text
' Can be made local to Shift method if needed
Function Modulus(dividend As Integer, divisor As Integer) As Integer
dividend = dividend Mod divisor
Return If(dividend < 0, dividend + divisor, dividend)
End Function
Function Shift(text As String, offset As Integer) As String
' validation omitted
Dim length = text.Length
Dim arr(length - 1) As Char
For i = 0 To length - 1
arr(Modulus(i + offset, length) Mod length) = text(i)
Next
Return new String(arr)
End Function
That way you can easily handle negative values or values greater than the length of the text.
Note, the same thing is possible with a StringBuilder instead of an array ; I'm not sure which one is "better"
Function Shift(text As String, offset As Integer) As String
Dim builder As New StringBuilder(text)
Dim length = text.Length
For i = 0 To length - 1
builder(Modulus(i + offset, length) Mod length) = text(i)
Next
Return builder.ToString
End Function
Using Gordon's code did the trick. The left function visual studio tried to create a stub of the function, so I used the fully qualified function name when calling it. But this worked perfectly.
intNumChars = shiftnumber.Text
output.Text = Mid(word, intNumChars + 1, Len(word)) & Microsoft.VisualBasic.Left(word, intNumChars)
n = 3
output.Text = Right(word, Len(word) - n) & Left(word, n)

Read binary values (such 1000, 1001, 1010, 1011...) as an array of digits

I am in search of a solution for the following task:
General environment: I am working on a toolbox of VBA functions to process data in an Access database of educational offers. This includes various string operations and operations with database fields mostly via SQL.
In one of my functions, I have to deal with different states of four database fields in a row, their values being NULL or not NULL. As each field can be either NULL or not NULL, we have 16 possible situations:
0-0-0-0, 0-0-0-1, 0-0-1-0, 0-0-1-1 and so on,
which is obviously quite the same as:
0000, 0001, 0010, 0011, 0100, ..., 1111, i.e. binary representation of decimal 0-15.
In order to not hard code every single case, I want make use of the binary representations of numbers 0-15 by counting up from 0 to 15.
In pseudocode:
i = 0
For i = 0 To 15
arrX(): 0000
StateOfField01 = arrX(0) [which is 0, in this case]
StateofField02 = arrX(1) [dito]
StateOfField03 = arrX(2) [dito]
StateofField04 = arrX(3) [dito]
Do something with the fields, depending on their state
i = i + 1
Next i
So far I am happy with my idea, but there is one thing I have no idea how to solve:
How can I get from the binary representations of i an array containing four digits each?
Here is a function which will convert a Decimal number to Binary String.
Public Function GetBinary(Number As Long) As String
'********************
'Code Courtesy of
' Paul Eugin
'********************
Dim resultStr As String, nLen As Integer
Do While Number > 0
resultStr = resultStr & (Number Mod 2)
Number = Int(Number / 2)
Loop
GetBinary = Format(StrReverse(resultStr), "0000")
End Function
The function will take in a "Number" as argument for which you want to find the Binary equivalent. The Format function at the end of the code will make sure the return would be a minimum of 4 literal representation. So if you pass the example,
? GetBinary(5)
0101
Based on PaulFrancis' input, I found the following solution answering my proper needs.
Important additions are 1) "ByVal" for the argument in the call so that the function can be called from inside another procedure whose for-next counter should be preserved; 2) a test for "Number" being 0 which was not covered previously.
Public Function GetBinary(ByVal Number As Integer) As String
Dim resultStr As String
If Number = 0 Then
resultStr = "0"
Else
Do While Number > 0
resultStr = resultStr & (Number Mod 2)
Number = Int(Number / 2)
Loop
End If
GetBinary = Format(StrReverse(resultStr), "0000")
End Function
'-----------------------------------
Public Sub TestBinaryToString()
'Purpose: Testing the GetBinary() function:
'output will be displayed in the Immediate Window
Dim i As Integer
Dim strBin As String
For i = 0 To 15
strBin = GetBinary(i)
Debug.Print i; " --> "; strBin
Next i
End Sub