Splitting a string based on a set length of characters - vb.net

MVC 3. Vb.net. Part of my app generates PDF files using Itextsharp. Some strings are too long to go onto the background image correctly. So I basically need to split this string when its over 26 characters long and when it splits it cant split in the middle of a word. from there I will use newline to add the string to the right to the next line... Any ideas that might point me in the right direction.. I did start bulding the function that I will pass the string into test for length and then pass back the string after it finishes but I am stummped after that..
Private Function stringLength(ByVal _string As String) As String
If _string.Length < 26 Then
_string.Split(
End If
End Function

I'm sure there's a million different ways to do this.
You basically need to get all of your words split by the space into a list. After that, you just need to keep checking if the current word plus a space plus the next word reach your threshold or not, and if it does, you move to the next line. Once you have all of your lines, then you rejoin the list into a single string again.
Private Function LimitWidth(ByVal text As String, ByVal maxCharacters As Integer) As String
Dim words As List(Of String) = text.Split(" "c).ToList()
If text.Length < maxCharacters OrElse words.Count = 1 Then
Return text
Else
Dim lines As New List(Of String)
Dim currentLine As String = words(0)
For i As Integer = 1 To words.Count - 1
If (currentLine & " " & words(i)).Length > maxCharacters Then
lines.Add(currentLine)
currentLine = words(i)
If i = words.Count - 1 Then
lines.Add(currentLine)
End If
Else
If i = words.Count - 1 Then
lines.Add(currentLine & " " & words(i))
End If
currentLine &= " " & words(i)
End If
Next
Return String.Join(Environment.NewLine, lines.ToArray())
End If
End Function
To Test:
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
MessageBox.Show(LimitWidth("This is a really long sentence " & _
"meant to demonstrate how to split " & _
"the words into a confined character length.", 26))
End Sub

It sounds like you are asking for a word wrap function.
Since I feel that it's better to answer in a way that promotes learning than to just give answers, I have for you a link that walks you through the process of using Test Driven Development (TDD) to solve this problem. It just so happens that the word wrap problem is a popular coding kata, and Robert C. Martin wrote a somewhat silly fictional story about a developer being taught how to use TDD to solve the word wrap kata.
The code examples are in Java, but it should be trivial to read and translate.
http://thecleancoder.blogspot.com/2010/10/craftsman-62-dark-path.html
The goofy bits are skip-able. Just jump down to the sentences right before the first code snippet.

I would add to it handling of multiline input text with following:
Private Function LimitWidth(ByVal text As String, ByVal maxCharacters As Integer, SplitSign As String) As String
Dim Output As String = ""
Dim OrgLines As List(Of String) = text.Split(Environment.NewLine).ToList()
For x As Integer = 1 To OrgLines.Count - 1
Dim words As List(Of String) = OrgLines(x).Split(" "c).ToList()
If text.Length < maxCharacters OrElse words.Count = 1 Then
Output += OrgLines(x)
Else
Dim lines As New List(Of String)
Dim currentLine As String = words(0)
For i As Integer = 1 To words.Count - 1
If (currentLine & " " & words(i)).Length > maxCharacters Then
lines.Add(currentLine)
currentLine = words(i)
If i = words.Count - 1 Then
lines.Add(currentLine)
End If
Else
If i = words.Count - 1 Then
lines.Add(currentLine & " " & words(i))
End If
currentLine &= " " & words(i)
End If
Next
Output += String.Join(SplitSign, lines.ToArray())
End If
Next
Return Output
End Function
use:
LimitWidth("your text", 80, Environment.NewLine)

Related

Delete first letter of the words using visual basic

I have a code, which can change last letters of words to the dot. I need to how, how to change the code, so when I write some words, in output I will get them without first letter?
for ex:
Input: Hello,how are you?
Output: ello, ow re ou?
Here is my code:
Sub New5
dim s, ns as String
dim r as String
s = inputbox("Input text")
r = "Inputed text:" & chr(9) & s & chr(13)
for i = 2 to len(s)
if mid(s,i,1)=" " then ns = ns + "." else ns = ns + mid(s,i-1,1)
next i
ns = ns + "."
r = r & "Result of work:" & chr(9) & ns
MsgBox r
End Sub
For VB6:
Private Sub Convert()
Dim strIn as string
Dim strA() As String
Dim strOut As String
Dim iX As Integer
strIn - "Hello, how are you?"
strA = Split(strIn, " ")
For iX = 0 To UBound(strA)
strA(iX) = Mid$(strA(iX), 2)
Next
strOut = Join(strA, " ")
End Sub
Incidentally your libreoffice tag is also inappropriate as LibreOffice doesn't use the same language as vb6 or vba.
Sorry, just saw this was tagged vb6. This is a vb.net answer.
If you want to get rid of the first letter of each word, the first thing to do is get the words. String.Split will return an array based on the split character you provide. In this case that character is a space. The small c following the string tells the compiler that this is Char.
Now we can loop through each word and cut off the first letter. I am storing the shortened words in a List(Of String). You can get rid of the first letter by using Substring passing the start index. We want to start at the second letter so we pass 1. Indexes start at 0 for the first letter.
Finally, use String.Join to put everything back together.
Chr, Len, Mid, and MsgBox are all left overs from VB6. They work for backward compatibility but it is a good idea to learn the .net classes functionality.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
New5()
End Sub
Private Sub New5()
Dim input = InputBox("Input text")
Dim words = input.Split(" "c)
Dim ShortWords As New List(Of String)
For Each word In words
ShortWords.Add(word.Substring(1))
Next
Dim shortenedString = String.Join(" ", ShortWords)
MessageBox.Show(shortenedString)
End Sub

VB: Add a space after a specific character into a string if it does not already have one

I want all of my strings to be formatted consistently. It is grabbing values from the database and sometimes there are not spaces after colons, but there should be. My goal is to add a space after a colon (:) if there isn't one already. I would prefer to do this without regex, but I am open to any solutions! Thanks so much.
Edit: Apologies I read the title as VBA, have updated now.
Here is a nice simple solution for you.
Replace everything with a Colon and a space with just a Colon meaning all Colons no longer have a space regardless of weather they did or not initially.
Then Replace all Colons with a Colon and a Space:
Dim value1 As String = "Hello:World"
value1 = value1.Replace(": ", ":").Replace(":", ": ")
I can't say it's the most elegant. And its performance is not optimized, though using String.IndexOf should in general be faster than looping over every character. But being a brute force solution, it sure doesn't use regex. ;)
Besides regex, I'm sure there's a cute one-line solution using LINQ, but it's probably hard to read and maintain. Someone is welcome to post that for comparison.
Option Strict On
Module Module1
Sub Main()
Console.WriteLine(EnsureSpaceAfterColon("first: second:asdf third::"))
'prints:
'"first: second: asdf third: : "
End Sub
Public Function EnsureSpaceAfterColon(input As String) As String
Dim colon As Char = CChar(":")
Dim space As Char = CChar(" ")
Dim returnString As String = String.Copy(input) 'leave the original alone
Dim index As Integer = input.IndexOf(colon)
While index > -1 'String.IndexOf returns -1 if the index is not found
'if the index is the last index, there is no space, so add it
'or else if the Char at the next index is not a space, make it so
If index = returnString.Length - 1 OrElse returnString.Chars(index + 1) <> space Then
returnString = returnString.Insert(index + 1, space)
End If
'get the next index
index = returnString.IndexOf(colon, index + 1)
End While
Return returnString
End Function
End Module
Public Module Module1
Public Sub Main()
dim nospace as string = EnsureSpace("xxx:")
dim space as string = EnsureSpace("xxx: ")
Console.WriteLine("|" + nospace + "|")
Console.WriteLine("|" + space + "|")
End Sub
private function EnsureSpace(val as string)
dim temp as string = val.trim
return temp.padright(temp.length + 1)
end function
' OR
private function EnsureSpace1(val as string)
return val.substring(0, val.LastIndexOf(":") + 1) + " "
end function
End Module
|xxx: |
|xxx: |
Three different approaches (all different from the other answers)...pick your poison:
(1) Using a helper Iterator function and String.Join():
Public Function AddSpaceAfterColons(ByVal input As String) As String
Return String.Join("", ColonHelper(input))
End Function
Public Iterator Function ColonHelper(ByVal input As String) As IEnumerable(Of Char)
Dim lastCh As Nullable(Of Char)
For Each ch As Char In input
If lastCh.HasValue AndAlso lastCh.Value = ":"c AndAlso Not (ch = " "c) Then
Yield " "c
End If
Yield ch
lastCh = ch
Next
If lastCh.HasValue AndAlso lastCh.Value = ":"c Then
Yield " "c
End If
End Function
(2) Walking backwards through a StringBuilder:
Public Function AddSpaceAfterColons(ByVal input As String) As String
Static colon As Char = ":"c
Static space As Char = " "c
Dim sb As New System.Text.StringBuilder(input)
For i As Integer = sb.Length - 1 To 0 Step -1
If sb(i) = colon Then
If i = (sb.Length - 1) OrElse sb(i + 1) = space Then
sb.Insert(i + 1, space)
End If
End If
Next
Return sb.ToString
End Function
(3) Using String.Split() and a StringBuilder:
Public Function AddSpaceAfterColons(ByVal input As String) As String
Dim sb As New System.Text.StringBuilder
Dim parts() As String = input.Split(":")
sb.Append(parts(0))
For i As Integer = 1 To parts.Length - 1
sb.Append(":")
If Not parts(i).StartsWith(" ") Then
sb.Append(" ")
End If
sb.Append(parts(i))
Next
Return sb.ToString
End Function

Split text between multiple delimiters

I have a rather tricky problem with my ongoing project.
I pretty much need to extract ceritain Strings between muliple delimiters out of a bigger String.
To give you a better understanding, what I mean, here is an example:
Some Text that wont be needed
Some Text that wont be needed
Some Text that wont be needed
Some Text that wont be needed
Some Text that wont be needed
Textstart (Start-Delimiter)
Text I want
Text I want
Text I want
Text I want
Text I want
Textend (End-Delimiter)
So far, so easy. But now comes a messy part in. The End-delimiters change sometimes like this
Textstart
Text I want
Text I want
Text I want
Text I want
Textend2 (another end delimiter)
I also solved that Problem, but now since I discovered, that the start delimiter can also occur twice before the next endpart.
Like this:
Textstart (Start-Delimiter)
Text I want
Text I want
Textstart
Text I want
Text I want
Textend (End-Delimiter)
This really is confusing to me. This is the function right now. It works but only if the start delimiter does not occur twice.
I could split the text first by the end strings and after that by the start string, but I don't know hot to split a text by multiple delimiters.
Function NewTextGet(ByVal Text As String, ByVal StartString As String, ByVal EndStrings() As String)
Dim AllBlocks As New List(Of String)
Dim FirstSplit() As String = Strings.Split(Text, StartString) ' Splits Text at Start delimiter
For Each splt In FirstSplit.Skip(1)
Dim EndSplit1 = splt.Split({EndStrings(0)}, StringSplitOptions.None) ' First end delimiter Split
Dim EndSplit2 = EndSplit1(0).Split({EndStrings(1)}, StringSplitOptions.None) ' Second delimiter Split
Dim EndSplit3 = EndSplit2(0).Split({EndStrings(2)}, StringSplitOptions.None) ' Third delimiter Split
If EndSplit3.Length > 1 Then
AllBlocks.Add(EndSplit3(0))
ElseIf EndSplit2.Length > 1 Then
AllBlocks.Add(EndSplit2(0))
Else
AllBlocks.Add(EndSplit1(0))
End If
Next
Return AllBlocks
End Function`
I hope I explained this well enough, and thank you for any help :)
This version produces a List(Of List(OF String)). So each set of lines will be in a different list:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim fileName As String = "C:\Users\mikes\Downloads\gYeziPRE.txt"
Dim blocks As List(Of List(Of String)) = NewTextGet(My.Computer.FileSystem.ReadAllText(fileName), "ctxt", New String() {"done", "sdone", "prompt"})
For i As Integer = 0 To blocks.Count - 1
Debug.Print("Block: " & i)
For Each line As String In blocks(i)
Debug.Print(line)
Next
Debug.Print("")
Next
End Sub
Function NewTextGet(ByVal Text As String, ByVal StartString As String, ByVal EndStrings() As String) As List(Of List(Of String))
Dim started As Boolean = False
Dim curBlock As List(Of String)
Dim AllBlocks As New List(Of List(Of String))
Dim lines() As String = Text.Split(Environment.NewLine)
For Each line As String In lines
If line.Contains(StartString) Then
If Not started Then
started = True
curBlock = New List(Of String)
End If
Dim i As Integer = line.IndexOf(StartString)
curBlock.Add(line.Substring(i + StartString.Length).Trim())
ElseIf EndStrings.Contains(line.Trim()) Then
started = False
If Not IsNothing(curBlock) Then
AllBlocks.Add(curBlock)
End If
curBlock = Nothing
ElseIf started = True AndAlso Not IsNothing(curblock) Then
curBlock.Add(line.Trim())
End If
Next
If Not IsNothing(curBlock) Then
AllBlocks.Add(curBlock)
End If
Return AllBlocks
End Function
Output:
Block: 0
"What's up?"
para "All these Trainers"
line "look the same, but"
para "only one is the"
line "leader!"
Block: 1
"Am I Koji?"
para "Why, yes, I am!"
Block: 2
"Well done!"
para "Here!"
para "The Fist Badge!"
Block: 3
"<PLAYER> received"
line "Fist Badge."
Block: 4
"Here!"
para "Take this TM!"
Block: 5
"Hah!"
para "That was joyful"
line "sparring!"
Block: 6
"Japanese"
line "onomatopoeia"
cont "are so kawaii!"
Block: 7
"Hiri hiri!"
Block: 8
"Uwaaaa!"
Block: 9
"Well, you chose"
line "unwisely."
Block: 10
"You have more"
line "chances."
Block: 11
"Koji is hot."
para "Dressing like him"
line "is<...>"
para "wonderful!"
Block: 12
"Wasn't supposed"
line "to happen!"
Block: 13
"Can't wait for"
line "Halloween!"
Block: 14
"Ninjas are so"
line "cool!"
Block: 15
"Not skilled"
line "enough!"
Block: 16
"Time to study"
line "ninjutsu instead"
cont "of pretending."
Try this
Function NewTextGet(ByVal RawText As String, ByVal StartString As String, ByVal EndStrings() As String) As List(Of String)
Dim bEnd As List(Of String) = EndStrings.ToList
bEnd.Insert(0, StartString)
Dim Blocks As New List(Of String)
Dim Splits() As String = Split(RawText, vbNewLine, , CompareMethod.Text)
For x As Integer = 0 To Splits.Length - 1
1:
Dim block As String = ""
If Splits(x).Contains(StartString) Then
block = Splits(x)
For y As Integer = x + 1 To Splits.Length - 1
Dim BlockEnd As Boolean = False
For Each s As String In bEnd
If Splits(y).Contains(s) Then BlockEnd = True
Next
block &= vbNewLine
If BlockEnd Then
If Splits(y).Contains(StartString) Then
Blocks.Add(block & vbNewLine)
x = y - 1
GoTo 1
End If
x = y + 1
block &= Splits(y)
Blocks.Add(block & vbNewLine)
Exit For
End If
block &= Splits(y)
Next
End If
Next
Return Blocks
End Function
usage
For Each s As String In NewTextGet(Raw, "ctxt", New String() {"sdone", "done", "prompt"})
TextBox2.Text &= s & "=======" & vbNewLine
Next
use this order {"sdone", "done", "prompt"} to avoid conflection while spliting

Encode and Decode VBA Program

in my programming class I have to create a program which allows the user in order to enter a sentence, with the buttons "Encode" and "Decode" as options. So, for "Encode", you have to translate the sentence into Asc numbers (already did this). However, I'm currently stuck on the "Decode" section, for you have to use a For loop and an array to separate the Asc numbers by spaces, then translate them into characters one by one. Here's what I have so far:
Public Class Form1
Dim Message As String
Dim NewMessage As String
Dim Part As String
Dim Part2 As Integer
Dim Letter As String
Dim Length As String
Dim ints() As Integer
Dim intlength As Integer
Private Sub btn_Enter_Click(sender As Object, e As EventArgs) Handles btn_Enter.Click
Message = txt_Message.Text
Length = Message.Length() - 1
If rbn_Encode.Checked = True Then
For Num As Integer = 0 To Length
Letter = Message(Num)
Me.lbl_Code.Text = lbl_Code.Text & Asc(Letter) & " "
Next
End If
If rbn_Decode.Checked = True Then
For Num As Integer = 0 To intlength Step 1
If Message(Num) <> " " Then
Part = Part & Message(Num)
Else
NewMessage = NewMessage & ChrW(Part) & " "
End If
Next
Me.lbl_Code.Text = NewMessage
End If
End Sub
Private Sub ExitToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles ExitToolStripMenuItem.Click
Application.Exit()
End Sub
End Class
I've been stuck on this for about 2 week, and I'm still clueless. Thank you for your help and have a wonderful day.
This might seem to veer off the topic of the question, but it all meanders towards a better answer.
OK there are a few issues with your code. Firstly, make sure that "Option Strict" is on - have a look here. This will force you to convert types properly and reduce the potential for problems. Bearing the above in mind,
Dim Length As String
should be
Dim Length As Integer
on to the next bit
Each procedure should have a single responsibility. Your btn_Enter.Click event handler includes code for encoding text and decoding numbers. These should be separated out into their own procedures. In a relatively short bit of code like yours, it's not too much of a problem, but even here, it makes things a little fuzzier. Have a look at the code below. There are more issues, but we'll look at them in a moment. The code below is a bit clearer and more maintainable.
Private Sub btn_Enter_Click(sender As Object, e As EventArgs) Handles btn_Enter.Click
Message = txt_Message.Text
Length = Message.Length() - 1
If rbn_Encode.Checked = True Then
EncodeTextToAscii()
End If
If rbn_Decode.Checked = True Then
DecodeToText()
End If
End Sub
Private Sub DecodeToText()
For Num As Integer = 0 To intlength Step 1
If Message(Num) <> " " Then
Part = Part & Message(Num)
Else
NewMessage = NewMessage & ChrW(Part) & " "
End If
Next
Me.lbl_Code.Text = NewMessage
End Sub
Private Sub EncodeTextToAscii()
For Num As Integer = 0 To Length
Letter = Message(Num)
Me.lbl_Code.Text = lbl_Code.Text & Asc(Letter) & " "
Next
End Sub
Next.
In your code to encode the string as ASCII, you store the resulting data directly in the label lbl_Code's text property. The user interface should never be used as the primary store for data. It's bad practice and potentially allows the user to change data accidentally - in textboxes for example. In the case of a label, it's not to important, but it's far better to get into the good habits.
To store your encoded ASCII numbers, you can use the array ints, but as your code stands, the declaration of ints is just that. There is no space in the array to store data. So, in the Encode procedure, you need to resize ints to the same as the number of characters in the string.
So now we have ..
Private Sub EncodeTextToAscii()
ReDim ints(Length)
For Num As Integer = 0 To Length
Letter = Message(Num)
ints(Num) = Asc(Letter)
Next
End Sub
Finally onto the meat of your question. The Decode procedure can now be written as this ..
Private Sub DecodeToText()
NewMessage = ""
For Each asciiNumber As Integer In ints
NewMessage = NewMessage & ChrW(asciiNumber) & " "
Next
Me.lbl_Code.Text = NewMessage
End Sub
Instead of mucking around getting the length of a loop and getting the ascii number in each element of an array, you can simplyfy it using a For Each statement. You dont need to know the length of the array. It just loops over the whole length. Much easier.
As an excercise, try applying the For Each idea to the Encode procedure. :-)

Writing a string to a new .csv in VB.net

I am trying to write a string to a .csv file, but unable to get it to display.
I have managed to do it in VBA, but when writing in VB.net it's not working.
I first create the file and set the headers for each column. After this I am getting information on each required attribute and writing it to a string s.
All i want to do now is write the string to the .csv file so that each attribute is in the right column under the right header.
Each time the string s simply needs to be on a new row.
This is what I have so far (I have cut out some bits of code so some syntax may look incorrect). What am i doing wrong or missing?
Sub Main()
Dim sOutput As String
' Create a header for the output file
sOutput = ("Level,Occurrence Name,Reference Name,Object type, Visibility, Path" & vbLf)
If Occs.Count > 0 Then
For i = 1 To Occs.Count
iLevel = 0
curOcc = Occs.Item(i)
GetOccurrenceData(curOcc, sOutput, oSel, False, iLevel)
Next
End If
' Write the output string to a file
Dim sPath As String
Dim bWrite As Boolean
sPath = ("C:\temp\data3.csv")
bWrite = WriteFile(sPath, sOutput)
End Sub
Sub GetOccurrenceData(curOcc As VPMOccurrence, s As String, sel As Selection, ByVal bParentHidden As Boolean, ByVal iParentLevel As Integer)
'CODE TO GET DATA REMOVED AS IRRELEVANT
' Append the output string with the data for the current occurrence.
s = (s & curLevel & "," & sName & "," & sRefName & "," & sType & "," & sVisibility & vbLf)
' Repeat this data gathering procedure on any children the current occurrence may have.
Occs = curOcc.Occurrences
If Occs.Count > 0 Then
For i = 1 To Occs.Count
GetOccurrenceData(Occs.Item(i), s, sel, bChildrenInheritNoShow, curLevel)
Next
End If
In GetOccurrenceData you pass in a string and change it in the method, but you did not pass it in as a ByRef so anything done to the string in the method stays in the method.
Change the header of your method to read
Sub GetOccurrenceData(curOcc As VPMOccurrence,ByRef s As String, sel As Selection, ByVal bParentHidden As Boolean, ByVal iParentLevel As Integer)
I would however recommend using a StringBuilder to accomplish what you are doing.
Like This:
Sub Main()
Dim sb As New Text.StringBuilder()
sb.AppendLine("Level,Occurrence Name,Reference Name,Object type, Visibility, Path")
If Occs.Count > 0 Then
For i = 1 To Occs.Count
iLevel = 0
curOcc = Occs.Item(i)
GetOccurrenceData(curOcc, sb, oSel, False, iLevel)
Next
End If
' Write the output string to a file
Dim sPath As String
Dim bWrite As Boolean
sPath = ("C:\temp\data3.csv")
bWrite = WriteFile(sPath, sb.ToString())
End Sub
Sub GetOccurrenceData(curOcc As VPMOccurrence, sb As Text.StringBuilder, sel As Selection, ByVal bParentHidden As Boolean, ByVal iParentLevel As Integer)
'CODE TO GET DATA REMOVED AS IRRELEVANT
' Append the output string with the data for the current occurrence.
sb.Append(curLevel).Append(",").Append(sName).Append(",").Append(sRefName).Append(",").Append(sType).Append(",").AppendLine(sVisibility)
' Repeat this data gathering procedure on any children the current occurrence may have.
Occs = curOcc.Occurrences
If Occs.Count > 0 Then
For i = 1 To Occs.Count
GetOccurrenceData(Occs.Item(i), sb, sel, bChildrenInheritNoShow, curLevel)
Next
End If
End Sub