|
By: Paul S Cilwa |
Posted: 4/10/2026 |
|
Page Views: 42 |
| Hashtags: #Namtira #VisualBasic #VBNET #ClassLibrary #NamtiraLib #SuperStrings |
| Creating extension methods to the String object type. |
| Estimated reading time: 9 minute(s) (2103 words) |
| Module SuperStrings |
| Member | Parameters | Example |
| Safe | — | myObj.Safe → "" if Nothing |
| Left | Length As Int16 | "Hello".Left(3) → "Hel" |
| Right | Length As Int16 | "Hello".Right(3) → "llo" |
| Mid | Start As Int16, Length As Int16 (optional) | "Hello".Mid(2, 3) → "ell" |
| IndexOf | SearchFor As String, Direction As SearchDirection (optional) | "abcabc".IndexOf("b", SearchDirection.Right2Left) |
| Remove | RemoveThese As Char() or RemoveThis As String | "a-b-c".Remove("-") → "abc" |
| Enquote | Q As String (optional) or Q1, Q2 As String | "Hi".Enquote → ""Hi"" |
| Dequote | Q As String (optional) or Q1, Q2 As String | ""Hi"".Dequote → "Hi" |
| LCase | — | "Hello".LCase → "hello" |
| UCase | — | "Hello".UCase → "HELLO" |
| TitleCase | — | "hello world".TitleCase → "Hello World" |
| SentenceCase | — | "hello world".SentenceCase → "Hello world" |
| ToBool | — | "Yes".ToBool → True |
| Wrap | LineLength As Integer (optional, default 70) | longText.Wrap(40) |
| ToHtml | — | "A & B".ToHtml → "A & B" |
| FromHtml | — | "A & B".FromHtml → "A & B" |
| ToJScript | — | "He said "hi"".ToJScript |
| FromJScript | — | jsText.FromJScript |
| TrimClean | — | dirtyText.TrimClean |
| StripAnchorTags | — | html.StripAnchorTags |
| Swap | ByRef a, ByRef b | Swap(x, y) |
| Constants |
| vbQuote | — | vbQuote → " |
Open the new NamtiraLib 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 SuperStrings 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 SuperStrings 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 data 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.
Searching and Removing
The built-in IndexOf method only searches left-to-right. Our extension
adds an optional Direction parameter, using a custom SearchDirection
enum, so we can also search right-to-left.
Enum SearchDirection
Left2Right
Right2Left
End Enum
<Extension()>
Public Function IndexOf(Item As String,
SearchFor As String,
Optional Direction As SearchDirection = SearchDirection.Left2Right) As Int16
If Direction = SearchDirection.Right2Left Then
Item = Item.Reverse
SearchFor = SearchFor.Reverse
End If
Return Item.IndexOf(SearchFor) - Item.Length + 1
End Function
Similarly, removing characters or substrings from a string is a common
operation that doesn't need to be verbose. The Remove extension has
two overloads: one that removes every character in a given array of characters,
and another that removes all occurrences of a given substring.
<Extension()>
Public Function Remove(Item As String, RemoveThese As Char()) As String
For Each C As Char In RemoveThese
Item = Item.Replace(C, "")
Next
Return Item
End Function
<Extension()>
Public Function Remove(Item As String, RemoveThis As String) As String
Return Item.Replace(RemoveThis, "")
End Function
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.
There are also overloads of Enquote and Dequote that accept
distinct opening and closing quote characters—useful when working with
parentheses, brackets, or other asymmetric delimiters.
<Extension()>
Public Function Enquote(Value As String, Q1 As String, Q2 As String) As String
Return Q1 + Value.Safe + Q2
End Function
<Extension()>
Public Function Dequote(Value As String, Q1 As String, Q2 As String) As String
If Value.Left(1) = Q1 And Value.Right(1) = Q2 Then
Value = Value.Mid(2, Value.Length - 2)
End If
Return Value
End Function
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
onto 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("&", "&")
PlainText = PlainText.Replace("—", "—")
PlainText = PlainText.Replace("—", "—")
PlainText = PlainText.Replace(ChrW(150), "—")
PlainText = PlainText.Replace("é", "é")
PlainText = PlainText.Replace(vbQuote, """)
PlainText = PlainText.Replace(ChrW(8220), """)
PlainText = PlainText.Replace(ChrW(8221), """)
PlainText = PlainText.Replace("'", "'")
PlainText = PlainText.Replace("…", "…")
PlainText = PlainText.Replace("…", "…")
PlainText = PlainText.Replace("½", "½")
PlainText = PlainText.Replace("¼", "¼")
PlainText = PlainText.Replace("¾", "¾")
PlainText = PlainText.Replace("ï", "ï")
PlainText = PlainText.Replace("™", "™")
PlainText = PlainText.Replace("\", "\")
PlainText = PlainText.Replace(vbCr, " ")
PlainText = PlainText.Replace(vbLf, " ")
PlainText = PlainText.Replace(vbTab, "	")
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("&", "&")
HtmlText = HtmlText.Replace("—", "—")
HtmlText = HtmlText.Replace("é", "é")
HtmlText = HtmlText.Replace(""", """")
HtmlText = HtmlText.Replace("'", "'")
HtmlText = HtmlText.Replace("…", "…")
HtmlText = HtmlText.Replace("½", "½")
HtmlText = HtmlText.Replace("¼", "¼")
HtmlText = HtmlText.Replace("¾", "¾")
HtmlText = HtmlText.Replace("ï", "ï")
HtmlText = HtmlText.Replace("™", "™")
HtmlText = HtmlText.Replace("\", "\")
HtmlText = HtmlText.Replace(" ", vbCr)
HtmlText = HtmlText.Replace(" ", vbLf)
HtmlText = HtmlText.Replace("	", 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
Cleaning Up Text
Sometimes you receive text from external sources that contains stray
carriage returns, line feeds, and tabs that you'd rather not have. The
TrimClean extension strips all carriage returns and line feeds,
replaces tabs with spaces, and trims the result.
<Extension>
Public Function TrimClean(Source As String) As String
Return Source.Replace(vbCr, "").Replace(vbLf, "").Replace(vbTab, " ").Trim()
End Function
Stripping Anchor Tags
When you need to extract plain text from HTML but want to preserve
everything except the anchor tags themselves, StripAnchorTags uses
regular expressions to remove both the opening <a> and
closing </a> tags while leaving the link text intact.
<Extension>
Function StripAnchorTags(Input As String) As String
Dim Result As String = Regex.Replace(Input, "<a\b[^>]*>", "", RegexOptions.IgnoreCase)
Result = Regex.Replace(Result, "</a>", "", RegexOptions.IgnoreCase)
Return Result
End Function
Swapping Variables
A classic utility that doesn't belong to any particular type but earns
its keep everywhere: Swap exchanges the values of two variables
by reference. Note that this is a Sub, not an extension method,
since it operates on a pair of values rather than extending a single type.
Public Sub Swap(ByRef a, ByRef b)
Dim Temp
Temp = b
b = a
a = Temp
End Sub