Replacing Characters Simultaneously - vb.net

Hey guys I'm trying to make a program that helps people encrypt messages and decrypt messages using the Caesar shift cipher, I know it's probably already been done, I want to have a go myself though.
The problem I've been having is when it comes to encrypting the text. The user selects a number (between 1-25) and then the application will change the letters corresponding to the number chosen, e.g. if the user inputs "HI" and selects 2, both characters are moved two places down the alphabet outputting "JK". My main problem is the replacing characters though, mostly because I've set up the program to be able to encrypt large blocks of text, because my code is:
If cmbxKey.Text = "1" Then
If txtOutput.Text.Contains("a") Then
sOutput = txtOutput.Text.Replace("a", "b")
txtOutput.Text = sOutput
End If
If txtOutput.Text.Contains("b") Then
sOutput = txtOutput.Text.Replace("b", "c")
txtOutput.Text = sOutput
End If
End If
This means if the user inputs "HAY" it will change it to "HBY" and then because of the second if statement it will change it to "HCY" but I only want it to be changed once. Any suggestions to avoid this???? Thanks guys

Since you want to shift all characters, start out by looping though the characters using something like ToArray:
For each s as string in txtOutput.Text.ToArray
'This will be here for each character in the string, even spaces
Next
Then, rather than having cases for every letter, look at it's ascii number:
ACS(s)
...and shift it by the number you want to. Keep in mind that if the number is greater than (I don't know if you want upper/lower case) 122, you want to subtract 65 to get you back to "A".
Then you can convert it back into a character using:
CHR(?)
So this might look something like this:
Dim sb as new text.StringBuilder()
For each s as string in txtOutput.Text.ToArray
If asc(s) > 122 Then
sb.append(CHR(ASC(s) + ?YourShift? - 65)
Else
sb.append(CHR(ASC(s) + ?YourShift?)
END IF
Next
txtOutput.Text = sb.ToString

A very simple method of changing your application while keeping your strategy is to replace the lower case characters with upper case characters. Then they won't be recognized by the Replace method anymore.
Obviously, the problem is that you want to implement an algorithm. In general, an algorithm should be smart in the sense that you don't have to do the grunt work. That's why a method such as the one presented by Steve is smarter; it doesn't require you to map each character separately, which is tedious, and - as most tedious tasks - error prone.

One big issue arise when you're facing a String that the basic Alphanumeric table can't handle. A String that contains words like :
"Déja vu" -> The "é" is going to be what ?
And also, how about encoding the string "I'm Aaron Mbilébé" if you use .ToUpper().
.ToUpper returns "I'M AARON MBILÉBÉ".
You've lost the casing, and how do you handle the shifting of "É" ?
Of course, a code should be smart as pointed above, and I was used to deal with strings just by using the System.Text.ASCIIEncoding to make things easier. But from the moment I started to use large amount of textual datas, sources from the web, files (...) I was forced to dig deeper, and seriously consider string encoding (and System Endianness by the way, when coding and decoding string to/from array of bytes)
Re-think of what do you really want in the end. If you're the only one to use your code, and you're certain that you'll only use A..Z, 0..9, a..z, space and a fixed amount of allowed characters (like puntuation) then, just build a Table containing each of those chars.
Private _AllowedChars As Char() = { "A"c, "B"c, ... "0"c, "1"c, .. "."c, ","c ... }
or
Private _AllowedChars As Char() = "ABCDEF....012...abcd..xyz.;,?:/".ToCharArray()
Then use
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
FinalChars = New Char(AllChars.Length - 1) {} ' It's VB : UpperBound is n+1 item.
' so n items is UpperBound - 1
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) + ShiftValue) Mod _AllowedChars.Length)
Next
Return New String(FinalChars)
End Function
And
Private Function UnShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
' ... the same code until :
FinalChars(i) = _AllowedChars((Array.IndexOf(_AllowedChars, AllChars(i)) - ShiftValue + _AllowedChars.Length) Mod _AllowedChars.Length)
' ...
End Function
^^ Assuming ShiftValue is always positive (defined once)
But again, this only works when you have a predefined set of allowed characters. If you want a more flexible tool, you ought to start dealing with encodings, array of byte, BitConverter and have a look at system endianness. That's why I asked if someone else is goind to use your application : let's try this string :
"Xin chào thế giới" ' which is Hello World in vietnamese (Google Trad)
In that case, you may give up..? No ! You ALWAYS have a trick in your cards !
Just create your allowed chars on the fly
Private _AllowedChars As New SortedList(Of Char, Char)
-> get the string to encode (shift)
Private Function ShiftChars(ByVal CurrentString As String, ByVal ShiftValue As Integer) As String
Dim AllChars As Char() = CurrentString.ToCharArray()
Dim FinalChars As Char()
Dim i As Integer
' Build your list of allowed chars...
_AllowedChars.Clear()
For i = 0 To AllChars.Length - 1
If Not _AllowedChars.ContainsKey(AllChars(i)) Then
_AllowedChars.Add(AllChars(i), AllChars(i))
End If
Next
' Then, encode...
FinalChars = New Char(AllChars.Length - 1) {}
For i = 0 To AllChars.Length - 1
FinalChars(i) = _AllowedChars.Keys.Item((_AllowedChars.IndexOfKey(AllChars(i)) + ShiftValue) Mod _AllowedChars.Count)
Next
Return New String(FinalChars)
End Function
The same for Unshift/decode.
Note : in foreing languages, the resulting string is pure garbage and totally unreadable, unless you (un)shift the chars again.
However, the main limitation of this workaround is the same as the fixed chars array above : Once you encode your string, and add a char in your encoded string that doesn't exists in the initial generated allowed chars, then you've nuked your data and you won't be able to decode your string. All you'll have is pure garbage.
So one day... one day maybe, you'll have to dig deeper at the byte level of the thing, in a defined extended encoding (Unicode/UTF8/16) to secure the integrity of your data.

Related

How to loop a sub w/ a variable value?

I'm creating a program that outputs a randomly generated password in which the user has jurasdiction over how many characters they want their password to be. How would i go about taking the input from the user and then looping the subroutine (which generates a single random character)? Any help would be much appreciated, thank you!
' this is the code for my input where n = the no. of characters the user wants in their pasword
Public Sub inp()
Console.WriteLine("How many characters do you want in your password?
(up to 20 allowed)")
n = Console.ReadLine()
End Sub
'this is the simple sub that generates a random number from which a character from my database is chosen
Public Sub randi()
Randomize()
i = Rnd() * 92
End Sub
Let us think about the steps envolved.
Ask user for a number
Place the response in a variable.
Create a random string with n characters
I looks like you have taken care of 1 and 2.
Now let's break down number 3 into tasks.
What characters do you want to include.
Uppercase letters? Lowercase letters? Numbers? Punctuation?
An ASCII table will tell you that there are readable characters from ASCII 33 to 126.
If you were to ask for a random number in the range 33 to 126 then change it to the character it represnts, it would be a start.
Luckily .net provides a Random class.
First we will need to get an instance of that class to use its methods.
Private rnd As New Random
Now we have a variable holding an instance of the random class so we can use any of the classed properties and methods.
There is a method, .Next(Int32, Int32), that will produce a random number in a range.
The first parameter is the bottom number in the range and the second is one more than the top number.
Going back to ASCII table we need a random number like this
rnd.Next(33, 125)
Next we need to change the result to a character.
Convert.ToChar(Ascii number)
The next problem is to get the number of characters the user asked for. Remember n.
We can do this is a For...Next loop.
Inside the loop we want to build a string with these random characters. This could be done with an ampersand equals but every time you changes a string the compiler has to throw away the old string and create an entirely new one because strings are immutable. To solve this .net has provided a StringBuilder class. We create an instance of this class and then use the .Append method to add the characters to the sb.
After the loop we change the StringBuilder to a regular string and return it to the calling code.
Public Sub Main()
Console.WriteLine("How many characters do you want in your password?
(up to 20 allowed)")
Dim n As Integer = CInt(Console.ReadLine())
Dim Pword As String = GetRandomString(n)
Console.WriteLine(Pword)
End Sub
Private rnd As New Random
Private Function GetRandomString(stringLength As Integer) As String
Dim sb As New StringBuilder
For c = 1 To stringLength
Dim newAscii = rnd.Next(33, 127)
Dim newChr = Convert.ToChar(newAscii)
sb.Append(newChr)
Next
Return sb.ToString
End Function
In a real application you would have to check if the user entered a valid number. An Integer.TryParse and a check for 0 would probably do it.

VB.Net - Encode string to hex

this is my code to convert string to hex
Function StringToHex(ByVal text As String) As String
Dim xhex As String
For i As Integer = 0 To text.Length - 1
xhex &= Asc(text.Substring(i, 1)).ToString("x").ToUpper
Next
Return xhex
End Function
I convert string file to hex with this code, but if size file more than 1MB my program is not responding
how to make this code more efficient for size file more than 1MB sorry my english is bad
As I said in my initial comment, your current approach is creating a new string each time you go through the For loop. Strings are immutable (can't be changed) in .NET - so for example if you have 3000 characters in the string, xHex = &a is going to create 3,000 strings, and that's just for the first part. Then you have a Substring, then a ToString and finally a ToUpper - so if my math is right, you're creating 4 strings for every character in the input string (so if you have 3,000 characters that 12,000 additional strings).
The call to Substring is unnecessary - you can treat the string as an array and access each character in the string as an array index, so now you would have:
xhex &= Asc(text(i)).ToString("x").ToUpper
You can also get rid of the call .ToUpper() by using an uppercase "X" in the call to .ToString() - so now you have:
xhex &= Asc(text(i)).ToString("X")
You could also make xhex a StringBuilder, and then you'd only be creating one additional string each time through the loop (the call to .ToString()). Putting that all together gives you this:
Dim xhex As StringBuilder = New StringBuilder()
For i As Integer = 0 To text.Length - 1
xhex.Append(Asc(text(i).ToString("X"))
Next
Return xhex.ToString()
That may help with the process, but if the string is really large you may still run into memory issues. IF the file is really large I'd recommend reading it using a Stream and processing the Stream one byte at a time (or several bytes at time, your choice).
I would also suggest Googling for VB.NET convert string to hex, as there are many examples of other ways to do this.

Extracting characters from an input string vb.net

Hey guys I'm stuck with this question. Please help.
I want to write a program that can extract alphabetical characters and special characters from an input string. An alphabetical character is any character from "a" to "z"(capital letters and numbers not included") a special character is any other character that is not alphanumerical.
Example:
string = hello//this-is-my-string#capetown
alphanumerical characters = hellothisismystringcapetown
special characters = //---#
Now my question is this:
How do I loop through all the characters?
(the for loop I'm using reads like this for x = 0 to strname.length)...is this correct?
How do I extract characters to a string?
How do I determine special characters?
any input is greatly appreciated.
Thank you very much for your time.
You could loop through each character as follows:
For Each _char As Char In strname
'Code here
Next
or
For x as integer = 0 to strname.length - 1
'Code here
Next
or you can use Regex to replace the values you do not need in your string (I think this may be faster but I am no expert) Take a look at: http://msdn.microsoft.com/en-us/library/xwewhkd1.aspx
Edit
The replacement code will look something as follows although I am not so sure what the regular expression (variable called pattern currently only replacing digits) would be:
Dim pattern As String = "(\d+)?" 'You need to update the regular expression here
Dim input As String = "123//hello//this-is-my-string#capetown"
Dim rgx As New Regex(pattern)
Dim result As String = rgx.Replace(input, "")
Since you need to keep the values, you'll want to loop through your string. Keeping a list of characters as a result will come in handy since you can build a fresh string later. Then take advantage of a simple Regex test to determine where to place things. The psuedo code looks something like this.
Dim alphaChars As New List(Of String)
Dim specialChars As New List(Of String)
For Each _char As Char in testString
If Regex.IsMatch(_char, "[a-z]")) Then
alphaChars.Add(_char)
Else
specialChars.Add(_char)
End If
Next
Then If you need to dump your results into a full string, you can simply use
String.Join(String.Empty, alphaChars.ToArray())
Note that this code makes the assumption that ANYTHING else than a-z is considered a special character, so if needs be you can do a second regular expression in your else clause to test for you special characters in a similar manner. It really depends on how much control you have over the input.

Generating next string from given string

I want to generate next character from given character.
Eg : given charater = "A" next charater "B" to be generated programatically.
Try this snippet:
chr(Asc(yourCharacter)+1)
You may be able to move from one code point in Unicode to the next, but that may not match the users expectations, depending on their native language, so if there's a small, limited set of characters in which you wish to work, it's probably worth just holding that as a constant string and using IndexOf to match the current character:
Private Const CharRange as String = "ABCDE"
Public Function NextChar(ByVal CurrentChar as String) as String
Return CharRange((CharRange.IndexOf(CurrentChar(0))+1) Mod CharRange.Length)
End Function
(I'm assuming you wish to loop back to the start when the last character is reached. That assumption also may not be true)
Unicode code point version (still may not be right in all circumstances, as mentioned above):
Public Function NextChar(ByVal CurrentChar As Char) As Char
Return Convert.ToChar(Convert.ToInt32(CurrentChar) + 1)
End Function

What is LenB actually doing on none string parameters

I have this bit of code that is being converted from vb6 to vb.net. I need to know what LenB is doing in this bit of code.
Dim singleValue As Single 'var for conversion use(4byte -> 1single)'
Dim bytes() As Byte
Dim valueB() As Byte 'this gets set elsewhere and is redim-d to its size'
For n = 0 To CDbl(ItemNumberCombo.Text) - 1
'bytes() -> single'
'UPGRADE_ISSUE: LenB function is not supported.'
ReDim bytes(LenB(singleValue) - 1)
bytes(3) = valueB(n * 4)
bytes(2) = valueB(n * 4 + 1)
bytes(1) = valueB(n * 4 + 2)
bytes(0) = valueB(n * 4 + 3)
'UPGRADE_ISSUE: LenB function is not supported.'
'UPGRADE_ISSUE: VarPtr function is not supported. '
Call memcpy(VarPtr(singleValue), VarPtr(bytes(0)), LenB(singleValue))
'display the result'
DText(n).Text = VB6.Format(singleValue, "0.000000E+00") 'CStr(singleValue)'
If DataSaveCheckBox.CheckState = 1 And FileNameText.Text <> "" Then
csvOutput = csvOutput & DText(n).Text & ","
End If
Next n
Am I right in thinking that bytes is always ReDim'ed to the same size? By the looks of it 4 elements.
Why then use LenB to ReDim if you could just use a number? And why ReDim in the loop at all?
LenB() returns the length in bytes of a variable. The most common example is for strings, where it returns the size of the string in bytes rather than the number of characters, regardless of character encoding. For other types it returns the size of an object- the size of a single being 4. The reason they would do this is that they wanted the code to survive if a future version of visual basic ever changed the size of a single (never mind hard-coding the number 4 when assigning to the byte array).
When upgrading LenB() to .Net, for strings use System.Text.Encoding.Unicode.GetBytes() to get an array already populated with your string text bytes. Remember that .Net always uses Unicode for strings internally. If you really need a different encoding there are a number of alternatives in the Encoding namespace. For other types use the BitConverter class. Either way, don't go line by line as the newer methods take away a lot of the busy work.
Here — I'll help you out with the conversion some:
(earlier)
Dim csvOutput As New StringBuilder()
(later)
Dim valueB() As Byte 'this gets set elsewhere and is redim-d to its size'
Dim singleValue As Single 'var for conversion
' Included because the original developer was concerned the size of a single could change
Dim singleSize As Integer = BitConverter.GetBytes(singleValue).Length
Dim NumberItems As Double
If Double.TryParse(ItemNumberCombo.Text, NumberItems) Then
For n As Integer = 0 To NumberItems - 1
singleValue = BitConverter.ToSingle(valueB, n * singleSize)
'display the result
DText(n).Text = singleValue.ToString("E6") 'CStr(singleValue)
If DataSaveCheckBox.CheckState = 1 AndAlso Not String.IsNullOrEmpty(FileNameText.Text) Then
csvOutput.Append(DText(n).Text & ",")
End If
Next n
Else
' Handle Invalid ComboBox value here- may not be an issue for you
End If
Note that this code also demonstrates a StringBuilder as a much better way to build your csv data, the AndAlso operator, the .TryParse() methods, String.IsNullOrEmpty(), and Standard Format Strings, all of which are intended to replace constructs or techniques from vb6.
Trying to explain bad code... is just an effort in futility.
Interesting way to fill a Single by loading a byte array.
The LenB function gives you the length in bytes of a variable.
Yes it will always return 4 when passed a Single variable type.
My guess for the redim is so the array gets initialized instead of preserved.
But since it then assigns all 4 bytes, it isn't technically needed and is probably just defensive programming. Defensive programming might also explain the LenB. In case Single changes size in the future.