Struggling with taking strings from a textfile (Visual Basic) - vb.net

I am trying to make a program that checks a stock file for all products and returns with the right information from an entered GTIN code (you will ((hopefully)) understand from looking at my code)
Public Class Form1
Dim FILE_NAME As String = "H:\Visual Studio 2013\Projects\Control Task 2 Barcode Orders\Control Task 2 Barcode Orders\bin\Debug\receipt_orders.txt"
Dim GTIN As String
Dim LineContaining As String
Dim quantity As Integer
Dim description As String
Dim singleamount As String
Dim singleprice As Double
Dim totalprices As Double
Private Sub btnEnterGTIN_Click(sender As Object, e As EventArgs) Handles btnEnterGTIN.Click
GTIN = getValidGTIN()
Call itemfinder(GTIN)
End Sub
Function getValidGTIN()
Dim GTIN, ValidGTINAmountCharacters As String
Dim GTINOK As Boolean
'Declaring variables.
GTINOK = False
Do
GTIN = InputBox("Enter the full GTIN Number (it should be 8 digits long)")
'Prompts the user to enter the GTIN.
ValidGTINAmountCharacters = Len(GTIN)
'Makes it so that the amount of characters of the GTIN is stored in the variable ValidGTINAmountCharacters.
If ValidGTINAmountCharacters = 8 Then GTINOK = True
'Makes it so the program will only accept an input if it was 8 characters long.
If Not IsNumeric(GTIN) Then GTINOK = False
'Makes it so that if any other character typed in apart from a number is not valid.
If Not GTINOK Then MsgBox("The GTIN Number isn't valid. It should be a 8 digit number. (Should not contain letters or symbols).")
'Makes it so that if the GTIN is not valid according to the above, a message appears saying it is invalid.
Loop Until GTINOK
Return GTIN
End Function
Private Sub itemfinder(ByVal GTIN As String)
Using reader As New IO.StreamReader("receipt_orders.txt")
While Not reader.EndOfStream
Dim line As String = reader.ReadLine()
If line.Contains(GTIN) Then
line = LineContaining
Exit While
End If
End While
End Using
description = Mid$(LineContaining, 10, 17)
singleamount = Mid$(LineContaining, 38, 4)
quantity = InputBox("Enter the amount required")
totalprices = quantity * singleamount
lstGTIN.Items.Add(GTIN)
lstName.Items.Add(description)
lstQuantity.Items.Add(quantity)
lstSinglePrice.Items.Add(singleamount)
lstTotal.Items.Add(totalprices)
Dim sum As Double
For x As Integer = 0 To lstTotal.Items.Count - 1
sum += Val(lstTotal.Items.Item(x).ToString)
Next
txtTotalPrice.Text = sum.ToString
End Sub
End Class
When I type in a quantity and the item code, i get an error code relating to the calculation of the total prices - i don't know how to fix this!
Also, the textfile i am using looks like this
12345670 L-Shaped Brackets 7.20
10101010 Television 1.80
69696969 Screws 0.20
Please try to explain how to solve this problem as simple as possible! I am not very adept with Visual Basic!

Some of the code in there, such as the use of the Call keyword, indicates a vb6/vbscript era mindset. Here is an implementation of the same functionality using the modern and cleaner VB.Net coding styles:
Public Class ProductItem
Public Property GTIN As Integer
Public Property Description As String
Public Property ItemPrice As Decimal
Protected Property SourceData As String
Public Shared Function FromLineString(ByVal lineString As String) As OrderLine
Return New ProductItem() With
{
.Description = lineString.SubString(9,17),
.ItemPrice = Decimal.Parse(lineString.SubString(37,4)),
.GTIN = Int32.Parse(lineString.SubString(0,8)), 'Guessed at this field
.SourceData = lineString
}
End Function
End Class
Public Class Form1
Const FILE_NAME As String = "H:\Visual Studio 2013\Projects\Control Task 2 Barcode Orders\Control Task 2 Barcode Orders\bin\Debug\receipt_orders.txt"
Private Sub btnEnterGTIN_Click(sender As Object, e As EventArgs) Handles btnEnterGTIN.Click
Dim item As ProductItem = itemfinder(Form1.InputValidGTIN())
If item Is Nothing Then Exit Sub ' May want to show error here
Dim QTY As Integer = Form1.InputInteger("Enter the amount required", "Input was not a valid Integer. Please try again.")
Me.SuspendLayout()
lstGTIN.Items.Add(item.GTIN)
lstName.Items.Add(item.Description)
lstQuantity.Items.Add(QTY)
lstSinglePrice.Items.Add(item.ItemPrice)
lstTotal.Items.Add(QTY * item.ItemPrice)
txtTotalPrice.Text = lstTotal.Items.Cast(Of Decimal).Sum().ToString()
Me.ResumeLayout()
End Sub
Public Shared Function InputValidGTIN() As String
Dim GTIN As String = InputBox("Enter the full GTIN Number (it should be 8 digits long)").Trim()
While Not IsValidGTIN(GTIN)
MsgBox("The GTIN Number isn't valid. It should be a 8 digit number. (Should not contain letters or symbols).")
GTIN = InputBox("Enter the full GTIN Number (it should be 8 digits long)").Trim()
End While
Return GTIN
End Function
Public Shared Function IsValidGTIN(ByVal GTIN As String) As Boolean
Static regex As New Regex("^\d{8}$")
Return regex.IsMatch(GTIN)
End Function
Public Shared Function InputInteger(ByVal PromptText As String, ByVal RePromptText As String) As Integer
Dim result As Integer
Dim input As String = InputBox(PromptText)
While Not Int32.TryParse(input, result)
input = InputBox(RePromptText)
End While
Return result
End Function
Private Function itemfinder(ByVal GTIN As String) As ProductItem
Using reader As New IO.StreamReader(FILE_NAME)
Dim line As String = reader.ReadLine()
While line IsNot Nothing
If line.Contains(GTIN) Then
Return ProductItem.FromLineString(line)
End If
line = reader.ReadLine()
End While
End Using
Return Nothing
End Function
End Class

Related

What is the fastest way to retrieve at runtime big powers of two in decimal string format?

In order to display big numbers I need to be able to retrieve the powers of two in decimal string format. The following code is the best I could do, is there a faster way?
Public NotInheritable Class TwoPowerOf
Private DecimalString As String = ""
Public Sub New(ByVal MyInteger As Integer)
If MyInteger > -256 And MyInteger < 1024 Then
Me.DecimalString = GetTwoPowerOf(MyInteger)
End If
End Sub
Public Overrides Function ToString() As String
Return Me.DecimalString
End Function
Protected Function GetTwoPowerOf(ByVal MyInteger As Integer) As String
Dim result As String = ""
Dim d As New Dictionary(Of Integer, String)
'...
d.Add(2, "4")
d.Add(1, "2")
d.Add(0, "1")
d.Add(-1, "0.5")
d.Add(-2, "0.25")
d.Add(-3, "0.125")
d.Add(-4, "0.0625")
d.Add(-5, "0.03125")
d.Add(-6, "0.015625")
d.Add(-7, "0.0078125")
d.Add(-8, "0.00390625")
' ...
result = d.Item(MyInteger)
Return result
End Function
End Class
And retreiving is something like this:
Sub whatever()
Dim MyInteger as Integer
Dim MyDecimalString as String = New TwoPowerOf(MyInteger).ToString
Console.WriteLine(MyDecimalString)
End Sub
Or, is there a way to convert big binary numbers into decimals without using the powers of two?

VB.NET - Randomize() with a function call in a string.replace method

I have a chat system and i want to put a "random string generator".
In my chat i have to write "%random%" and it is replaces with a random string.
I have a problem though, if i type "%random%%random%%random%" for example, it will generate the same string 3 times.
• Here is my function:
Public Function getRandomString(ByVal len As Integer) As String
Randomize()
Dim stringMap as string = "abcdefghijklmnopqrstuwvxyz0123456789"
Dim rndString As String = ""
Dim rnd As New Random()
For i As Integer = 0 To len - 1
Randomize()
rndString &= stringMap.Substring(rnd.Next(0, stringMap.Length), 1)
Next
Return rndString
End Function
• And here is my function call:
Dim msg As String = "Random string: %random%%random%%random%"
msg = msg.Replace("%random%", getRandomString(8))
MsgBox(msg)
The output for example: Random string: 5z15if725z15if725z15if72
I guess this is because it keeps the 1st return value in memory and pastes it, how can i fix that ?
Do i have to make a string.replace function myself ? Thanks
Oh no! You shouldn't call Randomize() here at all! Random is used in combination with the Rnd() function of VB. Creating a new Random object is enough here.
The reason you are getting the same results every time is because you are creating a new Random every time. You should reuse the same object to get different results.
'Create the object once
Private Shared rnd As New Random()
Public Function getRandomString(ByVal len As Integer) As String
Dim stringMap as string = "abcdefghijklmnopqrstuwvxyz0123456789"
Dim rndString As String = ""
For i As Integer = 0 To len - 1
rndString &= stringMap.Substring(rnd.Next(0, stringMap.Length), 1)
Next
Return rndString
End Function
EDIT: I realize that in addition to the above changes, you need to call the getRandomString function for every "%random%". String.Replace only calls the function once and pastes the result everywhere. With Regex, you could do something like this:
msg = new Regex("%random%").Replace(input, Function (match) getRandomString(8))
An easy way to do it is to find the first occurrence of "%random%", replace that, then repeat as necessary.
Written as a console application:
Option Infer On
Module Module1
Dim rand As New Random
Public Function getRandomString(ByVal len As Integer) As String
Dim stringMap As String = "abcdefghijklmnopqrstuwvxyz0123456789"
Dim rndString As String = ""
For i As Integer = 0 To len - 1
rndString &= stringMap.Substring(rand.Next(0, stringMap.Length), 1)
Next
Return rndString
End Function
Function ReplaceRandoms(s As String) As String
Dim stringToReplace = "%random%"
Dim r = s.IndexOf(stringToReplace)
While r >= 0
s = s.Remove(r, stringToReplace.Length).Insert(r, getRandomString(stringToReplace.Length))
r = s.IndexOf(stringToReplace)
End While
Return s
End Function
Sub Main()
Dim msg As String = "Random string: %random%%random%%random%"
msg = ReplaceRandoms(msg)
Console.WriteLine(msg)
Console.ReadLine()
End Sub
End Module

Appending a csv file

Dim NumberOfRecords As Integer
Sub Main()
Call ListTowns()
End Sub
Sub ListTowns()
Dim FileName As String
Dim MyFormat As String = "{0, -22} {1, -16} {2, -8} {3, -8}"
FileName = "Towns.csv"
Dim AllRecords As String() = System.IO.File.ReadAllLines(FileName)
Dim TownList = From record In AllRecords
Let field = record.Split(",")
Select New With {.Name = field(0), .County = field(1), .Population = field(2), .Area = field(3)}
For Each Town In TownList
Console.WriteLine(String.Format(MyFormat, Town.Name, Town.County, Town.Population, Town.Area))
Next
NumberOfRecords = TownList.Count
Console.ReadLine()
End Sub
Sub AddRecord()
Dim FileName As String
FileName = "C:\Users\Omar\Desktop\Towns.csv"
FileOpen(1, FileName, OpenMode.Random)
Dim NewRecord As String
Console.WriteLine("Enter the record you want add")
NewRecord = Console.ReadLine()
FilePut(1, NewRecord, NumberOfRecords + 1)
FileClose(1)
Console.WriteLine("The record has been added")
Console.ReadLine()
End Sub
At the moment the program can list the contents of the csv file however with regard to the AddRecord() Sub, when I input the data Test,Test,Test,Test it is adding this record to the file properly and is overwriting the first record.
How do I fix this?
OpenMode.Random is used for reading/writing files that have a fixed width record. It is unclear from your post if you actually have fixed width records (my hunch is you don't). At any rate, to properly read/write in random mode you'd have to declare a structure that defines the length of each record and also tell the system the length of the record when you open it (so that it can move that many bytes to the desired record position you want to read/write!). Follow the links listed in this page titled Random File Access to learn how to do this.
If you don't actually have fixed width records, but instead have variable length records (one record per line, and the lines are different lengths), then you can simply store all the lines in a List(Of String) and add the new record to the end of the List. Now you simply overwrite the entire file with System.IO.File.WriteAllLines(), similar to what you did with ReadAllLines(). With this approach, you can modify any of the records in the List and then simply overwrite the entire file to update the physical file.
Here's a quick example of what the second approach might look like:
Module Module1
Public AllRecords As New List(Of String)
Public FileName As String = "C:\Users\Omar\Desktop\Towns.csv"
Public Sub Main()
LoadTowns()
ListTowns()
AddRecord()
ListTowns()
End Sub
Public Sub LoadTowns()
AllRecords.Clear()
AllRecords.AddRange(System.IO.File.ReadAllLines(FileName))
End Sub
Public Sub ListTowns()
Dim MyFormat As String = "{0, -22} {1, -16} {2, -8} {3, -8}"
Dim TownList = From record In AllRecords
Let field = record.Split(",")
Select New With {.Name = field(0), .County = field(1), .Population = field(2), .Area = field(3)}
For Each Town In TownList
Console.WriteLine(String.Format(MyFormat, Town.Name, Town.County, Town.Population, Town.Area))
Next
Console.ReadLine()
End Sub
Public Sub AddRecord()
Console.WriteLine("Enter the record you want add")
Dim NewRecord As String = Console.ReadLine()
AllRecords.Add(NewRecord)
System.IO.File.WriteAllLines(FileName, AllRecords.ToArray)
Console.WriteLine("The record has been added")
Console.ReadLine()
End Sub
End Module

How to find a numeric value in a string

I am attempting to create a method which analyzes a string of text to see if it contains a numeric value. For instance, given the following string:
What is 2 * 2?
I need to determine the following information:
The string contains a numeric value: True
What is the numeric value that it contains: 2 (anyone of them should make the function return true and I should put the position of each of the 2's in the string in a variable such as position 0 for the first 2)
Here is the code I have so far:
Public Function InQuestion(question As String) As Boolean
' Possible substring operations using the position of the number in the string?
End Function
Here's an example console application:
Module Module1
Sub Main()
Dim results As List(Of NumericValue) = GetNumericValues("What is 2 * 2?")
For Each i As NumericValue In results
Console.WriteLine("{0}: {1}", i.Position, i.Value)
Next
Console.ReadKey()
End Sub
Public Class NumericValue
Public Sub New(value As Decimal, position As Integer)
Me.Value = value
Me.Position = position
End Sub
Public Property Value As Decimal
Public Property Position As Integer
End Class
Public Function GetNumericValues(data As String) As List(Of NumericValue)
Dim values As New List(Of NumericValue)()
Dim wordDelimiters() As Char = New Char() {" "c, "*"c, "?"c}
Dim position As Integer = 0
For Each word As String In data.Split(wordDelimiters, StringSplitOptions.None)
Dim value As Decimal
If Decimal.TryParse(word, value) Then
values.Add(New NumericValue(value, position))
End If
position += word.Length + 1
Next
Return values
End Function
End Module
As you can see, it passes the string `"What is 2 * 2?" and it outputs the positions and values of each numeric value:
8: 2
12: 2

How to correctly read a random access file in VB.NET

I am attempting to read a random access file, but I am getting the following error on the first file Error 5 (unable to read beyond end of the stream). I am not sure what I am doing wrong here, how might I fix this issue?
Structure StdSections
'UPGRADE_WARNING: Fixed-length string size must fit in the buffer. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="3C1E4426-0B80-443E-B943-0627CD55D48B"'
<VBFixedString(15), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=15)> Public A() As Char 'BEAM --- complete beam designation 15
'UPGRADE_WARNING: Fixed-length string size must fit in the buffer. Click for more: 'ms-help://MS.VSCC.v90/dv_commoner/local/redirect.htm?keyword="3C1E4426-0B80-443E-B943-0627CD55D48B"'
<VBFixedString(2), System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst:=2)> Public B() As Char 'DSG --- shape ie "W" or "C" 2
Dim C As Single 'DN --- nominal depth of section 4
Dim d As Single 'WGT --- weight 4
.
.
.
End structure
''Note 'File1'is the existing RAF and holds complete path!
Dim i,ffr,fLength,lastmembNo as integer
sectionFound = False
Dim std As new StdSections
fLength = Len(std)
If fLength = 0 Then fLength = 168 ' 177
ffr = FreeFile()
FileOpen(ffr, File1, OpenMode.Random, OpenAccess.Read, OpenShare.LockRead, fLength)
lastmembNo = CInt(LOF(ffr)) \ fLength
For i = 1 To lastmembNo
FileGet(ffr, std, i)
>>Error 5 (unable to read beyond end of the stream) <<<
If Trim(memberID) = Trim(std.A) Then
sectionFound = True
end if
next i
Wow Freefile! That's a blast from the past!
I haven't really used the old OpenFile etc. file access methods in VB.NET, so I'm just speculating, but in .NET many of the variable types got changed in size. e.g. an Integer is now 32-bits (4 bytes), I think a Boolean is different, though a Single is still 4 bytes.
Also, strings in .NET are by default in Unicode, not ASCII, so you cannot rely on 1 character=1 byte in a .NET String variable. In fact, .NET actualy "JIT compiles" programs on the PC before running, so you can't really lay out structures in memory easily like the old days.
If you want to switch to the new "Stream" based objects, here's some code to get you started:
Dim strFilename As String = "C:\Junk\Junk.txt"
Dim strTest As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Call My.Computer.FileSystem.WriteAllText(strFilename, strTest, False)
Dim byt(2) As Byte
Using fs As New FileStream(strFilename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)
fs.Seek(16, SeekOrigin.Begin)
fs.Read(byt, 0, 3)
Dim s As String = Chr(byt(0)) & Chr(byt(1)) & Chr(byt(2))
MsgBox(s)
fs.Seek(5, SeekOrigin.Begin)
fs.Write(byt, 0, 3)
End Using
Dim strModded As String = My.Computer.FileSystem.ReadAllText(strFilename)
MsgBox(strModded)
I wouldn't blame you for keeping the old method though: with the new method, you'll need to define a class, and then have a custom routine to convert from Byte() to the properties of the class. More work than simply loading bytes from the file into memory.
OK, I think you should switch to the ".NET way", as follows:
Imports System.IO
Imports System.Xml
Public Class Form1
Public Const gintRecLen_CONST As Integer = 177
Class StdSections2
Private mstrA As String
Public Property A() As String
Get
Return mstrA
End Get
Set(ByVal value As String)
If value.Length <> 15 Then
Throw New Exception("Wrong size")
End If
mstrA = value
End Set
End Property
Private mstrB As String
Public Property B() As String
Get
Return mstrB
End Get
Set(ByVal value As String)
If value.Length <> 2 Then
Throw New Exception("Wrong size")
End If
mstrB = value
End Set
End Property
Public C(39) As Single
Public Shared Function FromBytes(byt() As Byte) As StdSections2
Dim output As New StdSections2
If byt.Length <> gintRecLen_CONST Then
Throw New Exception("Wrong size")
End If
For i As Integer = 0 To 14
output.mstrA &= Chr(byt(i))
Next i
For i As Integer = 15 To 16
output.mstrB &= Chr(byt(i))
Next i
For i As Integer = 0 To 39
Dim bytTemp(3) As Byte
output.C(i) = BitConverter.ToSingle(byt, 17 + 4 * i)
Next i
Return output
End Function
End Class
Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim strFilename As String = "C:\Junk\Junk.txt"
Dim strMemberID As String = "foo"
Dim intRecCount As Integer = CInt(My.Computer.FileSystem.GetFileInfo(strFilename).Length) \ gintRecLen_CONST
Dim blnSectionFound As Boolean = False
Using fs As New FileStream(strFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
For intRec As Integer = 0 To intRecCount - 1
Dim intRecPos As Integer = gintRecLen_CONST * intRec
fs.Seek(intRecPos, SeekOrigin.Begin)
Dim byt(gintRecLen_CONST - 1) As Byte
fs.Read(byt, 0, gintRecLen_CONST)
Dim ss2 As StdSections2 = StdSections2.FromBytes(byt)
'MsgBox(ss2.A & ":" & ss2.C(3)) 'debugging
If strMemberID.Trim = ss2.A.Trim Then
blnSectionFound = True
Exit For
End If
Next intRec
End Using
MsgBox(blnSectionFound.ToString)
End Sub
End Class
We define a class called StdSections2 which uses .NET strings and an array of Singles. We use 0-based arrays everywhere. We load the file using the new FileStream object, and use the Seek() command to find the position we want. We then load raw bytes out of the file, and use Chr() and BitConverter.ToSingle() to convert the raw bytes into strings and singles.
I'm not sure about your example but this one however, works:
Public Class Form1
Const maxLenName = 30
Structure person
<VBFixedString(maxLenName)> Dim name As String
Dim age As Byte
End Structure
Private Sub Form1_Load(sender As [Object], e As EventArgs) Handles MyBase.Load
Dim entryIn As New person
Dim recordLen As Integer = Len(entryIn)
Dim entry As person
If FileIO.FileSystem.FileExists("test.raf") Then Kill("test.raf")
FileOpen(1, "test.raf", OpenMode.Random,,, recordLen)
'write
entry.name = LSet("Bill", maxLenName)
entry.age = 25
FilePut(1, entry, 6) 'write to 6th record
'read
Dim nRecords As Integer = LOF(1) \ recordLen
FileGet(1, entryIn, nRecords)
FileClose(1)
Dim personName As String = RTrim(entryIn.name)
Dim personAge As Byte = entryIn.age
MsgBox(personName & "'s age is " & personAge)
End Sub
End Class