I am new to VB.NET and would like to split a string into an array.
I have a string like:
613,710,200,127,127,'{\"js\":{\"\":\"16\",\"43451\":\"16\",\"65815\":\"16\",\"43452\":\"16\",\"41147\":\"16\",\"43449\":\"16\",\"43467\":\"16\",\"1249\":\"16\",\"43462\":\"16\",\"43468\":\"48\",\"43438\":\"64\",\"43439\":\"80\"}}','rca',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','xx.xx.xx.xx',NULL
I want to split this into a array at ",".
I tried:
Dim variable() As String
Dim stext As String
stext = "mystringhere"
variable = Split(stext, ",")
My problem is the part of
'{\"js\":{\"\":\"16\",\"43451\":\"16\",\"65815\":\"16\",\"43452\":\"16\",\"41147\":\"16\",\"43449\":\"16\",\"43467\":\"16\",\"1249\":\"16\",\"43462\":\"16\",\"43468\":\"48\",\"43438\":\"64\",\"43439\":\"80\"}}',
is split too. I want this to get all together in variable(5). Is this posible?
thank you for help
What you need is a CSV parser in which you can set the field quote character. Unfortunately the TexFieldParser which comes with VB.NET doesn't have that facility. Fortunately, other ones do - here I have used the LumenWorksCsvReader, which is available as a NuGet package *.
Option Strict On
Option Infer On
Imports System.IO
Imports LumenWorks.Framework.IO.Csv
Module Module1
Sub Main()
Dim s = "613,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','rca',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','xx.xx.xx.xx',NULL"
Using sr As New StringReader(s)
Using csvReader = New CsvReader(sr, delimiter:=","c, quote:="'"c, escape:="\"c, hasHeaders:=False)
Dim nFields = csvReader.FieldCount
While csvReader.ReadNextRecord()
For i = 0 To nFields - 1
Console.WriteLine(csvReader(i))
Next
End While
End Using
End Using
Console.ReadLine()
End Sub
End Module
which outputs
613
710
200
127
127
{"js":{"":"16","43451":"16","65815":"16","43452":"16","41147":"16","43449":"16","43467":"16","1249":"16","43462":"16","43468":"48","43438":"64","43439":"80"}}
rca
95
2048000
3
1
AABBCCDDEEFFGGHHIIJJKKLL=
xx.xx.xx.xx
NULL
Note that the double-quotes are doubled up in the literal string as that is the way to enter a single double-quote in VB.
If you really want the backslashes to remain, remove the escape:="\"c parameter.
If you are reading from a file then use the appropriate StreamReader instead of the StringReader.
Using the above, perhaps you have a Windows Forms program where you wanted to populate a RichTextBox with the data from, say, a text file named "C:\temp\CsvFile.txt" with the content
613,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','rca',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','xx.xx.xx.xx',NULL
614,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','din',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','yy.yy.yy.yy',NULL
615,710,200,127,127,'{\""js\"":{\""\"":\""16\"",\""43451\"":\""16\"",\""65815\"":\""16\"",\""43452\"":\""16\"",\""41147\"":\""16\"",\""43449\"":\""16\"",\""43467\"":\""16\"",\""1249\"":\""16\"",\""43462\"":\""16\"",\""43468\"":\""48\"",\""43438\"":\""64\"",\""43439\"":\""80\""}}','jst',95,2048000,3,1,'AABBCCDDEEFFGGHHIIJJKKLL=','zz.zz.zz.zz',NULL
you could use the above to come up with
Imports System.IO
Imports LumenWorks.Framework.IO.Csv
Public Class Form1
Public Class Datum
Property A As Integer
Property B As Integer
Property C As Integer
Property D As Integer
Property E As Integer
Property JsonData As String
Property SocketType As String
Property F As Integer
Property G As Integer
Property H As Integer
Property I As Integer
Property Base64Data As String
Property IpAddy As String
Property J As String
Public Overrides Function ToString() As String
Return $"{A}, {SocketType}, {IpAddy}, {B} ,{C}, {D}, {E}, {F}, {G}, {H}, {I}, {JsonData}, {Base64Data}, {J}"
End Function
End Class
Public Function GetData(filename As String) As List(Of Datum)
Dim data As New List(Of Datum)
Using sr As New StreamReader(filename)
Using csvReader = New CsvReader(sr, hasHeaders:=False, delimiter:=","c, quote:="'"c, escape:="\"c, comment:=Nothing, trimmingOptions:=ValueTrimmingOptions.UnquotedOnly)
Dim nFields = csvReader.FieldCount
If nFields <> 14 Then
Throw New MalformedCsvException("Did not find 14 fields in the file " & filename)
End If
While csvReader.ReadNextRecord()
Dim d As New Datum()
d.A = Integer.Parse(csvReader(0))
d.B = Integer.Parse(csvReader(1))
d.C = Integer.Parse(csvReader(2))
d.D = Integer.Parse(csvReader(3))
d.E = Integer.Parse(csvReader(4))
d.JsonData = csvReader(5)
d.SocketType = csvReader(6)
d.F = Integer.Parse(csvReader(7))
d.G = Integer.Parse(csvReader(8))
d.H = Integer.Parse(csvReader(9))
d.I = Integer.Parse(csvReader(10))
d.Base64Data = csvReader(11)
d.IpAddy = csvReader(12)
d.J = csvReader(13)
data.Add(d)
End While
End Using
End Using
Return data
End Function
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim srcFile = "C:\temp\CsvData.txt"
Dim dat = GetData(srcFile)
For Each d In dat
RichTextBox1.AppendText(d.ToString() & vbCrLf)
Next
End Sub
End Class
It might be necessary to perform more checks on the data when trying to parse it. Note that I made a function for the .ToString() method of the Datum class and put the properties in a different order just to demonstrate its use.
* Tools -> NuGet Package Manager -> Manage NuGet Packages for Solution... Choose the "Browse" tab -> type in LumenWorksCsvReader -> select the one by Sébastien Lorion et al., -> tick your project name in the pane to the right -> click Install.
I am new to VB.NET and would like to split a string into an array.
...
variable = Split(stext,",")
Instead of
variable = Split(stext,",")
use
variable = stext.split(",")
If you want to get a bit more complicated on your split you would create an array of char data as such
dim data(3) as char
data(0) = ","c
data(1) = vbcrlf
data(2) = chr(34)
data(3) = vbtab
... and so on
variable = stext.split(data)
I have the following class :
Public Class titlesclass
Public Property Link As String
Public Property Title As String
Public Function Clear()
Link.Distinct().ToArray()
Title.Distinct().ToArray()
End Function
End Class
And the following code :
For Each title As Match In (New Regex(pattern).Matches(content)) 'Since you are only pulling a few strings, I thought a regex would be better.
Dim letitre As New titlesclass
letitre.Link = title.Groups("Data").Value
letitre.Title = title.Groups("Dataa").Value
lestitres.Add(letitre)
'tempTitles2.Add(title.Groups("Dataa").Value)
Next
I tried to delete the duplicated strings using the simple way
Dim titles2 = lestitres.Distinct().ToArray()
And calling the class function :
lestitres.Clear()
But the both propositions didn't work , i know that i'm missing something very simple but still can't find what it is
Easier to use a class that already implements IComparable:
Dim query = From title In Regex.Matches(content, pattern).Cast(Of Match)
Select Tuple.Create(title.Groups("Data").Value, title.Groups("Dataa").Value)
For Each letitre In query.Distinct
Debug.Print(letitre.Item1 & ", " & letitre.Item2)
Next
or Anonymous Types:
Dim query = From title In Regex.Matches(content, pattern).Cast(Of Match)
Select New With {Key .Link = title.Groups("Data").Value,
Key .Title = title.Groups("Dataa").Value}
For Each letitre In query.Distinct
Debug.Print(letitre.Link & ", " & letitre.Title)
Next
Ok, Since I notice you are using a ClassHere is one option you can do in order to not add duplicate items to your List within a class.I'm using a console Application to write this example, it shouldn't be too hard to understand and convert to a Windows Form Application if need be.
Module Module1
Sub Main()
Dim titlesClass = New Titles_Class()
titlesClass.addNewTitle("myTitle") ''adds successfully
titlesClass.addNewTitle("myTitle") '' doesn't add
End Sub
Public Class Titles_Class
Private Property Title() As String
Private Property TitleArray() As List(Of String)
Public Sub New()
TitleArray = New List(Of String)()
End Sub
Public Sub addNewTitle(title As String)
Dim added = False
If Not taken(title) Then
Me.TitleArray.Add(title)
added = True
End If
Console.WriteLine(String.Format("{0}", If(added, $"{title} has been added", $"{title} already exists")))
End Sub
Private Function taken(item As String) As Boolean
Dim foundItem As Boolean = False
If Not String.IsNullOrEmpty(item) Then
foundItem = Me.TitleArray.Any(Function(c) -1 < c.IndexOf(item))
End If
Return foundItem
End Function
End Class
End Module
Another option would be to use a HashSet, It will never add a duplicate item, so even if you add an item with the same value, it wont add it and wont throw an error
Sub Main()
Dim titlesClass = New HashSet(Of String)
titlesClass.Add("myTitle") ''adds successfully
titlesClass.Add("myTitle") '' doesn't add
For Each title As String In titlesClass
Console.WriteLine(title)
Next
End Sub
With all of that aside, have you thought about using a Dictionary so that you could have the title as the key and the link as the value, that would be another way you could not have a list (dictionary) contain duplicate items
I have the following tests.And Classes. Now I just need to find how to write the rules and it seemed so simple ;-). But I'm going nowhere fast. As the tags say, I would like to use and learn piglet for this.
Public Class Plant
Public Property Genus As String
Public Property Species As String
Public Property SubSpecies As String
Public Property IsHybrid As Boolean
End Class
Public Class ParserTests
<Test>
Public Sub IfGenusCanBeFoundWhenOnlyGenusAndSpiecesAreThere()
Dim parser = New ParseLatinPlantName
Dim result = parser.Parse("Salvia sylvatica")
Assert.AreEqual("Salvia", result.Genus)
End Sub
<Test>
Public Sub IfSpeciesCanBeFoundWhenOnlyGenusAndSpiecesAreThere()
Dim parser = New ParseLatinPlantName
Dim result = parser.Parse("Salvia sylvatica")
Assert.AreEqual("sylvatica", result.Species)
End Sub
<Test>
Public Sub IfSubSpeciesCanBeFoundWhenSubSpeciesIsProvided()
Dim parser = New ParseLatinPlantName
Dim result = parser.Parse("Salvia sylvatica sp. crimsonii")
Assert.AreEqual("crimsonii", result.SubSpecies)
End Sub
<Test>
Public Sub IfIsHybridIsTrueWhenxIsInNameCanBeFoundWhenSubSpeciesIsProvided()
Dim parser = New ParseLatinPlantName
Dim result = parser.Parse("Salvia x jamensis")
Assert.IsTrue(result.IsHybrid)
End Sub
End Class
And here is what I tried so far.
Public Class ParseLatinPlantName
Public Function Parse(ByVal name As String) As Plant
Dim config = ParserFactory.Fluent()
Dim expr = config.Rule()
Dim name1 = config.Expression()
name1.ThatMatches("[a-z]+").AndReturns(Function(f) f)
Dim space1 = config.Expression()
space1.ThatMatches(" ").AndReturns(Function(f) f)
expr.IsMadeUp.By(name).As("Genus").Followed.By(name).As("Species").WhenFound(Function(f) New Plant With {.Genus = f.Genus})
Dim parser = config.CreateParser()
Dim result = DirectCast(parser.Parse(name), Plant)
Return result
End Function
End Class
Update
I got the first two tests passing thanks to Randompunter.
Public Class ParseLatinPlantName
Public Function Parse(ByVal name As String) As Plant
Dim config = ParserFactory.Fluent()
Dim expr = config.Rule()
Dim name1 = config.Expression()
name1.ThatMatches("\w+").AndReturns(Function(f) f)
expr.IsMadeUp.By(name1).As("Genus") _
.Followed.By(name1).As("Species") _
.WhenFound(Function(f) New Plant With {.Genus = f.Genus, .Species = f.Species})
Dim parser = config.CreateParser()
Dim result = DirectCast(parser.Parse(name), Plant)
Return result
End Function
End Class
Firstly, your original (then corrected expression matched only lowercase letters). This was corrected by changing it to \w+ which matched any other letter.
You second two tests failed because your grammar does not allow for more than two following letters. You will need to add a rule to make this work.
For instance, you have an example where a subspecies is provided. Assume that this takes the form where .sp xxx is an optional thing to pass, a separate rule needs to provided for this.
This passes the test for an optional subspecies
Public Class ParseLatinPlantName
Public Function Parse(ByVal name As String) As Plant
Dim config = ParserFactory.Fluent()
Dim expr = config.Rule()
Dim subSpecies = config.Rule()
Dim sp = config.Expression()
sp.ThatMatches("sp\.").AndReturns(Function(f) f)
Dim name1 = config.Expression()
name1.ThatMatches("\w+").AndReturns(Function(f) f)
Dim nothing1 = config.Rule()
expr.IsMadeUp.By(name1).As("Genus") _
.Followed.By(name1).As("Species") _
.Followed.By(subSpecies).As("Subspecies") _
.WhenFound(Function(f) New Plant With {.Genus = f.Genus, .Species = f.Species, .SubSpecies = f.Subspecies})
subSpecies.IsMadeUp.By(sp).Followed.By(name1).As("Subspecies").WhenFound(Function(f) f.Subspecies) _
.Or.By(nothing1)
Dim parser = config.CreateParser()
Dim result = DirectCast(parser.Parse(name), Plant)
Return result
End Function
End Class
Excuse my probably extremely shoddy VB, it was ages ago. Note that there is an expression that explicitly matches "sp." to distinguish it from any other type of name. This rule is then also matched by another rule that matches nothing. This enables the subspecies part to be optional.
I'm not to sure what you want parsed from the hybrid rule. I assume it must be something with name followed by an x and followed by some other name then it is a hybrid. To match this, add another rule to your parser.
The following parser passes all of your tests:
Public Class ParseLatinPlantName
Public Function Parse(ByVal name As String) As Plant
Dim config = ParserFactory.Fluent()
Dim expr = config.Rule()
Dim subSpecies = config.Rule()
Dim hybridIndicator = config.Expression
hybridIndicator.ThatMatches("x").AndReturns(Function(f) f)
Dim sp = config.Expression()
sp.ThatMatches("sp\.").AndReturns(Function(f) f)
Dim name1 = config.Expression()
name1.ThatMatches("\w+").AndReturns(Function(f) f)
Dim nothing1 = config.Rule()
expr.IsMadeUp.By(name1).As("Genus") _
.Followed.By(name1).As("Species") _
.Followed.By(subSpecies).As("Subspecies") _
.WhenFound(Function(f) New Plant With {.Genus = f.Genus, .Species = f.Species, .SubSpecies = f.Subspecies}) _
.Or.By(name1).As("FirstSpecies").Followed.By(hybridIndicator).Followed.By(name1).As("SecondSpecies") _
.WhenFound(Function(f) New Plant With {.IsHybrid = True})
subSpecies.IsMadeUp.By(sp).Followed.By(name1).As("Subspecies").WhenFound(Function(f) f.Subspecies) _
.Or.By(nothing1)
Dim parser = config.CreateParser()
Dim result = DirectCast(parser.Parse(name), Plant)
Return result
End Function
End Class
It is important that your expressions if they overlap are declared in the order of precedence. If you were to declare name1 before hybridIndicator the x would be recognized as a name, causing the parsing to fail. And as you probably noticed, Piglet ignores whitespace by default, there is no need to make a rule for it. If this setting is not desired, there is an option to turn it off in the configurator. (use the Ignore method)
How can I get the name of the object that was passed byref into a method?
Example:
Dim myobject as object
sub mymethod(byref o as object)
debug.print(o.[RealName!!!!])
end sub
sub main()
mymethod(myobject)
'outputs "myobject" NOT "o"
end sub
I'm using this for logging. I use one method multiple times and it would be nice to log the name of the variable that I passed to it. Since I'm passing it byref, I should be able to get this name, right?
For minitech who provided the answer:
This would give you the parameter name in the method and it's type, but not the name of the variable that was passed byref.
using system.reflection
Dim mb As MethodBase = MethodInfo.GetCurrentMethod()
For Each pi As ParameterInfo In mb.GetParameters()
Debug.Print("Parameter: Type={0}, Name={1}", pi.ParameterType, pi.Name)
Next
If you put that in "mymethod" above you'd get "o" and "Object".
That's impossible. Names of variables are not stored in IL, only names of class members or namespace classes. Passing it by reference makes absolutely zero difference. You wouldn't even be able to get it to print out "o".
Besides, why would you ever want to do that?
Alternatively you could get the 'Type' of the object using reflection.
Example: (Use LinqPad to execute)
Sub Main
Dim myDate As DateTime = DateTime.Now
MyMethod(myDate)
Dim something As New Something
MyMethod(something)
End Sub
Public Class Something
Public Sub New
Me.MyProperty = "Hello"
End Sub
Public Property MyProperty As String
End Class
Sub MyMethod(Byref o As Object)
o.GetType().Name.Dump()
End Sub
Sorry to say, but this is your solution. I left (ByVal o As Object) in the method signature in case you're doing more with it.
Sub MyMethod(ByVal o As Object, ByVal name As String)
Debug.Print(name)
End Sub
Sub Main()
MyMethod(MyObject, "MyObject")
End Sub
Alternatively you could create an interface, but this would only allow you to use MyMethod with classes you design. You can probably do more to improve it, but as this code stands you can only set the RealName at creation.
Interface INamedObject
Public ReadOnly Property RealName As String
End Interface
Class MyClass
Implements INamedObject
Public Sub New(ByVal RealName As String)
_RealName = RealName
End Sub
Private ReadOnly Property RealName As String Implements INamedObject.RealName
Get
Return _RealName
End Get
End Property
Private _RealName As String
End Class
Module Main
Sub MyMethod(ByVal o As INamedObject)
Debug.Print(o.RealName)
End Sub
Sub Main()
Dim MyObject As New MyClass("MyObject")
MyMethod(MyObject)
End Sub
End Module
If your program is still in the same place relative to the code that made it, this may work:
' First get the Stack Trace, depth is how far up the calling tree you want to go
Dim stackTrace As String = Environment.StackTrace
Dim depth As Integer = 4
' Next parse out the location of the code
Dim delim As Char() = {vbCr, vbLf}
Dim traceLine As String() = stackTrace.Split(delim, StringSplitOptions.RemoveEmptyEntries)
Dim filePath As String = Regex.Replace(traceLine(depth), "^[^)]+\) in ", "")
filePath = Regex.Replace(filePath, ":line [0-9]+$", "")
Dim lineNumber As String = Regex.Replace(traceLine(depth), "^.*:line ", "")
' Now read the file
Dim program As String = __.GetStringFromFile(filePath, "")
' Next parse out the line from the class file
Dim codeLine As String() = program.Split(delim)
Dim originLine As String = codeLine(lineNumber * 2 - 2)
' Now get the name of the method doing the calling, it will be one level shallower
Dim methodLine As String = Regex.Replace(traceLine(depth - 1), "^ at ", "")
Dim methodName = Regex.Replace(methodLine, "\(.*\).*$", "")
methodName = Regex.Replace(methodName, "^.*\.", "")
' And parse out the variables from the method
Dim variables As String = Regex.Replace(originLine, "^.*" & methodName & "\(", "")
variables = Regex.Replace(variables, "\).*$", "")
You control the depth that this digs into the stack trace with the depth parameter. 4 works for my needs. You might need to use a 1 2 or 3.
This is the apparently how Visual Basic controls handle the problem.
They have a base control class that in addition to any other common properties these controls may have has a name property.
For Example:
Public MustInherit Class NamedBase
Public name As String
End Class
Public Class MyNamedType
Inherits NamedBase
public Value1 as string
public Value2 as Integer
End Class
dim x as New MyNamedType
x.name = "x"
x.Value1 = "Hello, This variable is name 'x'."
x.Value2 = 75
MySubroutine(x)
public sub MySubroutine(y as MyNamedType)
debug.print("My variable's name is: " & y.name)
end sub
The output in the intermediate window should be:
My variable's name is: x