|
-
May 18th, 2022, 12:00 PM
#1
Thread Starter
Member
String Split Remove SOME Empty Entries
I'm trying to create an extension method for wrapping text to a specific number of characters for display in a console application. For the most part, it works very well, but I've run into a slight issue with how it handles a string that contains line breaks (Environment.NewLine, vbCr, vbLf, vbCrLf). Here's the method I'm currently working with:
vbnet Code:
<Extension()>
Public Function Wrap(ByVal Text As String, ByVal Width As Integer, Optional ByVal FullWidthLine As Boolean = False) As String
Dim InputLines As List(Of String) = Text.Split(New Char() {vbCr, vbLf, vbCrLf, Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries).ToList
Dim OutputLines As New List(Of String)
For Each Line As String In InputLines
Dim Words As List(Of String) = Line.Split(CChar(" ")).ToList()
If Line.Length < Width OrElse Words.Count = 1 Then
If FullWidthLine Then
OutputLines.Add(Line.PadRight(Width, " "c))
Else
OutputLines.Add(Line)
End If
Else
Dim CurrentLine As String = Words(0)
For I As Integer = 1 To Words.Count - 1
If (CurrentLine & " " & Words(I)).Length > Width Then
If FullWidthLine Then
OutputLines.Add(CurrentLine.PadRight(Width, " "c))
Else
OutputLines.Add(CurrentLine)
End If
CurrentLine = Words(I)
If I = Words.Count - 1 Then
If FullWidthLine Then
OutputLines.Add(CurrentLine.PadRight(Width, " "c))
Else
OutputLines.Add(CurrentLine)
End If
End If
Else
If I = Words.Count - 1 Then
If FullWidthLine Then
OutputLines.Add((CurrentLine & " " & Words(I)).PadRight(Width, " "c))
Else
OutputLines.Add(CurrentLine & " " & Words(I))
End If
End If
CurrentLine &= " " & Words(I)
End If
Next I
End If
Next
Return String.Join(Environment.NewLine, OutputLines.ToArray())
End Function
For a string like $"Invalid selection. Option {UserInput} is not available." & vbCrLf & "Please enter the number of the desired menu item from the list below." This works exactly as expected. The output is "wrapped" to two lines and displayed pretty much exactly as it was entered:
Code:
Invalid selection. Option 9 is not available.
Please enter the number of the desired menu item from the list below.
However, if I want to add an additional line of space between the two lines like this: $"Invalid selection. Option {UserInput} is not available." & vbCrLf & vbCrLf & "Please enter the number of the desired menu item from the list below." the StringSplitOptions.RemoveEmptyEntries wipes out my "extra" line and I end up with exactly the same thing as before:
Code:
Invalid selection. Option 9 is not available.
Please enter the number of the desired menu item from the list below.
Now, this is to be expected because the extra line break is "technically" an empty entry. But, in this case, I actually want that extra bit of space between the two lines.
Code:
Invalid selection. Option 9 is not available.
Please enter the number of the desired menu item from the list below.
If I remove the StringSplitOptions.RemoveEmptyEntries parameter from my Split method call (or change it to StringSplitOptions.None), then I end up with additional empty lines in my OutputLines List(Of String) object that "don't belong" there.
So, now I'm just trying to figure out how to build my List(Of String) object, split on any of the new line character(s), preserving blank lines, but not "creating" empty entries.
In my testing, I've tried to do this:
vbnet Code:
Dim NewLines As Char() = {vbCrLf, vbCr, vbLf, Environment.NewLine}
Dim TestLines As New List(Of String)
Dim LastIndex As Integer = 0
Dim NextIndex As Integer = Text.IndexOfAny(NewLines, LastIndex)
Do While NextIndex > 0
TestLines.Add(Text.Substring(LastIndex, NextIndex))
LastIndex = NextIndex + 1
NextIndex = Text.IndexOfAny(NewLines, LastIndex)
Loop
But, that ends up creating "duplicate" lines in my list because it's hitting each of the individual new line characters - vbCr (Chr(13)) and vbLf (Chr(10)) - and creating a new line for that. I tried just using Environment.NewLine or vbCrLf, but those don't seem to catch things correctly either and I end up with just two lines - everything up to the first line break, then one for everything after the new line characters at the beginning. It's still not what I'm after and I'm just not sure how to get there. Any assistance would be greatly appreciated.
Last edited by G_Hosa_Phat; May 18th, 2022 at 12:13 PM.
-
May 18th, 2022, 12:18 PM
#2
Re: String Split Remove SOME Empty Entries
So, if I'm reading right... sometimes you want to remove the empty lines ... other times not ... Would an optional flag PreserveEmpty work? Default to false. False would include the RemoveemptyEntries, while True wouldn't.
-tg
-
May 18th, 2022, 12:43 PM
#3
Thread Starter
Member
Re: String Split Remove SOME Empty Entries
Sorta, but sorta not. Let me see if I can explain it a bit better:
Here's the string I'm testing with:
$"Invalid selection. Option {UserInput} is not available." & vbCrLf & vbCrLf & "Please enter the number of the desired menu item from the list below."
Using the String.Split() method with the StringSplitOptions.None option, I get the following five entries added to my list:
[0] "Invalid selection. Option 9 is not available."
[1] ""
[2] ""
[3] ""
[4] "Please enter the number of the desired menu item from the list below."
With the StringSplitOptions.RemoveEmptyEntries option, I get just two entries added to the list:
[0] "Invalid selection. Option 9 is not available."
[1] "Please enter the number of the desired menu item from the list below."
What I WANT is three entries in the list like this:
[0] "Invalid selection. Option 9 is not available."
[1] ""
[2] "Please enter the number of the desired menu item from the list below."
Does that help clarify what I'm trying to accomplish?
ADDITIONAL TESTING:
I almost thought I had the solution by using Regex.Split() instead of String.Split():
Code:
Dim InputLines As List(Of String) = System.Text.RegularExpressions.Regex.Split(Text, vbCrLf).ToList
This worked great for my original testing string above, but it "failed" when I changed the vbCrLf characters to just vbLf. Because I don't want it to break apart the vbCrLf into two separate new line characters but I still want it to split on any individual new line characters, I guess I need to find a way to "normalize" the line breaks so the Split() method (whichever one works best in this case) can pick them up properly. Unless there's a way to "prioritize" the separators in a split operation so it looks for the vbCrLf before trying to find either the vbLf or vbCr.
Of course, now that I'm thinking of it, I suppose I could do it in two passes: the first pass splits on the vbCrLf, then it goes through each of those lines and splits them on either the vbCr or vbLf...
Last edited by G_Hosa_Phat; May 18th, 2022 at 12:52 PM.
-
May 18th, 2022, 01:12 PM
#4
Re: String Split Remove SOME Empty Entries
How often will you have a CR or LF that's not a CRLF?
-tg
-
May 18th, 2022, 01:26 PM
#5
Thread Starter
Member
Re: String Split Remove SOME Empty Entries
If I'm writing the code, virtually never. But, I'm trying to build this as an extension in a library that might be used by someone else or might be taking input from a source (like a file or something) where I can't necessarily control what line break characters are coming in.
But... I think I've found a solution. It's a little "wonky", but it seems to work for the situation. I'm "normalizing" the input text by converting all new line characters to vbLf:
Code:
Dim NormalizedText As String = Text.Replace(vbCrLf, vbLf).Replace(vbCr, vbLf)
This way, I don't have to worry about it breaking up a vbCrLf and creating "extra" entries. Once the text is "normalized", I then use the Regex.Split() method from above to split it on the vbLf character only, and voilà, I have three entries in my list!
Code:
Dim InputLines As List(Of String) = System.Text.RegularExpressions.Regex.Split(NormalizedText, vbLf).ToList
Even if I change the testing string to use a combination of vbCrLf, vbLf, or vbCr, it seems to finally be doing what I was trying to achieve! 
So, here's what I've come up with:
vbnet Code:
<Extension()>
Public Function Wrap(ByVal Text As String, ByVal Width As Integer, Optional ByVal FullWidthLine As Boolean = False) As String
Dim NormalizedText As String = Text.Replace(vbCrLf, vbLf).Replace(vbCr, vbLf)
Dim InputLines As List(Of String) = System.Text.RegularExpressions.Regex.Split(NormalizedText, vbLf).ToList
Dim OutputLines As New List(Of String)
For Each Line As String In InputLines
Dim Words As List(Of String) = Line.Split(CChar(" ")).ToList()
If Line.Length < Width OrElse Words.Count = 1 Then
If FullWidthLine Then
OutputLines.Add(Line.PadRight(Width, " "c))
Else
OutputLines.Add(Line)
End If
Else
Dim CurrentLine As String = Words(0)
For I As Integer = 1 To Words.Count - 1
If (CurrentLine & " " & Words(I)).Length > Width Then
If FullWidthLine Then
OutputLines.Add(CurrentLine.PadRight(Width, " "c))
Else
OutputLines.Add(CurrentLine)
End If
CurrentLine = Words(I)
If I = Words.Count - 1 Then
If FullWidthLine Then
OutputLines.Add(CurrentLine.PadRight(Width, " "c))
Else
OutputLines.Add(CurrentLine)
End If
End If
Else
If I = Words.Count - 1 Then
If FullWidthLine Then
OutputLines.Add((CurrentLine & " " & Words(I)).PadRight(Width, " "c))
Else
OutputLines.Add(CurrentLine & " " & Words(I))
End If
End If
CurrentLine &= " " & Words(I)
End If
Next I
End If
Next
Return String.Join(Environment.NewLine, OutputLines.ToArray())
End Function
I suppose I just needed to talk it out a bit.
Tags for this Thread
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|