View Sidebar

A Million Little Pieces Of My Mind

OrganicaLib: SuperStrings

By: Paul S. Cilwa Viewed: 7/22/2024
Posted: 1/21/2022
Updated: 2/2/2022
Page Views: 1256
Topics: #Organica #VisualBasic #VB.NET #ClassLibrary #OrganicaLib #SuperStrings
Creating extension methods to the String object type.

The low-level classes provided by .NET 5.0 are nevertheless very powerful. However, they inexplicably omit some also basic, and very useful properties and methods. While the effect of these missing elements can be performed by another combination of elements, doing so doesn't usually make for more readable code. And that's where extension methods and properties come in.

Open the new OrganicaLib project (if it isn't already open). Since this is the first module, created by default, we need to change "Class Class1" to "Module SuperStrings", in both the file and the filename, itself.

For those who don't like to type, here is the actual so-far contents of SuperStrings.vb.

Imports System.Runtime.CompilerServices Imports System.IO Imports System.Globalization Module SuperStrings End Module

Substrings

It's easy to confuse the String class with the Strings module, but they are quite distinct. The String class includes many useful methods, but no ability to return a substring of itself. For that, the programmer must use functions from the Strings module.

This is not, of course, an object-oriented approach. Luckily, it's easily remedied with a few extension methods.

Public Module SuperStrings <Extension()> Public Function Safe(ByVal Value) As String If Value Is Nothing Then Return String.Empty Else Return Value.ToString End If End Function <Extension()> Public Function Left(ByVal Value As String, ByVal Length As Int16) As String Return Strings.Left(Value.Safe, Length) End Function <Extension()> Public Function Right(ByVal Value As String, ByVal Length As Int16) As String Return Strings.Right(Value.Safe, Length) End Function <Extension()> Public Function Mid(ByVal Value As String, ByVal Start As Int16, Optional ByVal Length As Int16 = 0) As String If Length = 0 Then Return Strings.Mid(Value.Safe, Start) Else Return Strings.Mid(Value.Safe, Start, Length) End If End Function End Module

The Safe extension method actually works on any date type. Some return values like Null that will throw an exception. By running all values through the Safe method before otherwise working on them, we can avoid such an embarrassing result.

Left, Right, and Mid simply pass on the arguments to the appropriate Strings functions. This is a very small amount of work at this level, to prevent a lot of extra typing later.

Quoted Strings

I find I often need to apply quotes to a string, or to remove them. The String class doesn't provide a shortcut for these functions, but we can extend the class by adding three methods and a constant.

Module SuperStrings . . . Public Const vbQuote As String = """" <Extension()> Public Function Enquote(ByVal Value As String, Optional ByVal Q As String = vbQuote) As String Value = Value.Safe.Replace(Q, Q + Q) Return Q + Value.Safe + Q End Function <Extension()> Public Function Dequote(ByVal Value As String, Optional ByVal Q As String = vbQuote) As String Value = Value.Safe If Value.Left(1) = Q And Value.Right(1) = Q Then Value = Value.Mid(2, Value.Length - 2) Value = Value.Safe.Replace(Q + Q, Q) End If Return Value End Function End Module

Our constant, vbQuote, provides one I swear was in the older versions of Visual Basic.

The Enquote method more than simply concatenates a quote with the string and another quote. First, it checks and doubles any internal quotes, thus creating a proper Visual Basic quoted string.

Similarly, the Dequote method, first checking to ensure that this is, in point of fact, a quoted string. If it is, the enclosing quotes are removed, and any embedded double-quotes, replace with singles, thus restoring the original string.

Also, please note that any character can be used as "quotes"; the second argument to each method is optional, and only defaults to vbQuote.

Modifying Case

Upper case? Lower case? These are easy extensions making use of the Strings module, as we did earlier with substrings. Title case and sentence case are slightly trickier.

Module SuperStrings . . . <Extension()> Public Function LCase(ByVal Value As String) As String Return Strings.LCase(Value.Safe) End Function <Extension()> Public Function UCase(ByVal Value As String) As String Return Strings.UCase(Value.Safe) End Function <Extension()> Public Function TitleCase(ByVal Value As String) As String Return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(Value.Safe) End Function <Extension()> Public Function SentenceCase(ByVal Value As String) As String Return Value.Safe.Left(1).UCase + Value.Safe.Mid(2).LCase End Function End Module

Fail-safe Boolean

VB provides internal conversions to and from String objects and Boolean (true/false) objects. However, these conversions are simplistic and don't cover all data types. Thus we provide a ToBool method:

Module SuperStrings . . . <Extension()> Public Function ToBool(ByVal Value) As Boolean If Value Is Nothing Then Return False End If If VarType(Value) = vbBoolean Then Return Value End If If VarType(Value) = vbInteger Or VarType(Value) = vbLong Then Return If(Value = 0, False, True) End If If VarType(Value) = vbString Then Value = Value.Safe.Trim End If Select Case Value.Ucase Case "TRUE", "YES", "ON", "1", "T", "Y" ToBool = True Case Else ToBool = False End Select End Function End Module

Wrapping Text

Another obvious, but omitted, function that we might ask of text is wrapping—that is, if the text contains more than some arbitrary number of characters (say, 70), it should be split between words into multiple lines so that none exceed that limit.

<Extension()> Public Function Wrap(ByVal Text As String, Optional ByVal LineLength As Integer = 70) As String Text = Text.Trim.Replace(vbCrLf, " ").Replace(vbCr, " ").Replace(vbLf, " ").Replace(vbTab, " ") Dim Words As String() = Text.Split(" ") Dim i As Integer = 0 Dim Line As New StringBuilder With New StringBuilder While i < Words.Count If i > 0 Then .AppendLine() While (i < Words.Count) AndAlso (Line.Length + Words(i).Length + 1) < LineLength If Line.Length > 0 Then Line.Append(" ") End If Line.Append(Words(i)) i += 1 End While .Append(Line.ToString) Line.Clear() End While Return .ToString() End With End Function

HTML Conversions

If you've worked with websites at all, you know that there are a number of characters that are supposed to be represented by special HTML-specific substitutes. For example, since both the quote and double-quote characters are used in HTML tags to specify option values, you can't just plop text omtp a web page's HTML if it contains any of these characters.

SuperStrings to the rescue! The ToHtml extension will convert plain text to its HTML-safe equivalent.

<Extension()> Public Function ToHtml(ByVal PlainText As String) As String PlainText = PlainText.Replace("\""", "—SuperString=vbQuote—") PlainText = PlainText.Replace("\'", "—SuperString=vbApos—") PlainText = PlainText.Replace("&", "&amp;") PlainText = PlainText.Replace("—", "&mdash;") PlainText = PlainText.Replace("—", "&mdash;") PlainText = PlainText.Replace(ChrW(150), "&mdash;") PlainText = PlainText.Replace("é", "&eacute;") PlainText = PlainText.Replace(vbQuote, "&quot;") PlainText = PlainText.Replace(ChrW(8220), "&quot;") PlainText = PlainText.Replace(ChrW(8221), "&quot;") PlainText = PlainText.Replace("'", "&apos;") PlainText = PlainText.Replace("…", "&hellip;") PlainText = PlainText.Replace("…", "&hellip;") PlainText = PlainText.Replace("½", "&frac12;") PlainText = PlainText.Replace("¼", "&frac14;") PlainText = PlainText.Replace("¾", "&frac34;") PlainText = PlainText.Replace("ï", "&iuml;") PlainText = PlainText.Replace("™", "&trade;") PlainText = PlainText.Replace("\", "&#92;") PlainText = PlainText.Replace(vbCr, "&#13;") PlainText = PlainText.Replace(vbLf, "&#10;") PlainText = PlainText.Replace(vbTab, "&#9;") PlainText = PlainText.Replace("—SuperString=vbQuote—", "\""") PlainText = PlainText.Replace("—SuperString=vbApos—", "\'") Return PlainText End Function

Similarly, the FromHtml extension will convert an HTML-friendly block of text back into plain text.

<Extension()> Public Function FromHtml(ByVal HtmlText As String) As String HtmlText = HtmlText.Replace("&amp;", "&") HtmlText = HtmlText.Replace("&mdash;", "—") HtmlText = HtmlText.Replace("&eacute;", "é") HtmlText = HtmlText.Replace("&quot;", """") HtmlText = HtmlText.Replace("&apos;", "'") HtmlText = HtmlText.Replace("&hellip;", "…") HtmlText = HtmlText.Replace("&frac12;", "½") HtmlText = HtmlText.Replace("&frac14;", "¼") HtmlText = HtmlText.Replace("&frac34;", "¾") HtmlText = HtmlText.Replace("&iuml;", "ï") HtmlText = HtmlText.Replace("&trade;", "™") HtmlText = HtmlText.Replace("&#92;", "\") HtmlText = HtmlText.Replace("&#13;", vbCr) HtmlText = HtmlText.Replace("&#10;", vbLf) HtmlText = HtmlText.Replace("&#9;", vbTab) Return HtmlText End Function

And Then There Are JScript Strings

A situation that is similar in form but distinct in details comes when you're working on some Java, Javascript, or JScript code and need to insert some text with punctuation or other special characters in it. For the same reason as above with HTML text, we need to be able to convert plain text back and forth from Jscript-friendly text.

<Extension()> Public Function ToJScript(PlainText As String) As String Dim i As Int16 Dim Result As String = String.Empty For i = 1 To PlainText.Length Select Case PlainText(i - 1) Case """" Result += "\""" Case "'" Result += "\'" Case "\" Result += "\\'" Case vbCr Result += "\n" Case vbLf Result += "\f" Case vbTab Result += "\t" Case Else Result += PlainText(i - 1) End Select Next Return Result End Function <Extension()> Public Function FromJScript(ByVal JavaText As String) As String JavaText = JavaText.Replace("\""", """") JavaText = JavaText.Replace("\'", "'") JavaText = JavaText.Replace("\\", "\") JavaText = JavaText.Replace("\n", vbCr) JavaText = JavaText.Replace("\f", vbLf) JavaText = JavaText.Replace("\t", vbTab) Return JavaText End Function

Base Name of a File

The Fileinfo class provides easy access to the full pathname, the extension, and the basename plus extension…but not the basename, itself! Although technically this is an extension to the Fileinfo class, it returns a String and is used in many of the sane situations; so I'm including it here, rather than create a specialized module for it.

Module SuperStrings . . . <Extension()> Public Function BaseName(F As FileInfo) As String Return F.Name.Left(F.Name.Length - F.Extension.Length) End Function End Module

OrganicaLib Test Bed: SuperStrings

By: Paul S. Cilwa Posted: 1/23/2022
Updated: 1/30/2022
Page Views: 1288
Topics: #Organica #VisualBasic #VB.NET #ClassLibrary #OrganicaLib #SuperStrings
Testing String Theory.

Because the SuperStrings module contains several extensions (mostly to the String class), I envision displaying the results of calling each, in an HTML table. To that end, since building each table row will be a similar job, I'll start by creating a little Private function to build the HTML for each row.

Read more…