Skip to content

Commit d1867ec

Browse files
author
jeffshumphreys@gmail.com
committed
Made string functions consistent
All my string functions should respond the same for the same inputs. empty inputs return input as output. pass-thru behavior. Seeking for null values returns null because searching for a null is non-logical in strings. \0 is different.
1 parent 6d35e27 commit d1867ec

File tree

3 files changed

+136
-44
lines changed

3 files changed

+136
-44
lines changed

MySQLCLRFunctions.sln

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySQLCLRFunctions", "MySQLC
77
EndProject
88
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestMySQLCLRFunctions", "TestMySQLCLRFunctions\TestMySQLCLRFunctions.csproj", "{A084D459-BEE6-46BB-BB2C-4756879B2599}"
99
EndProject
10+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{912B914B-FAA0-4CB8-B38C-202E9B9965ED}"
11+
ProjectSection(SolutionItems) = preProject
12+
SQLCLR_Register.sql = SQLCLR_Register.sql
13+
EndProjectSection
14+
EndProject
1015
Global
1116
GlobalSection(SolutionConfigurationPlatforms) = preSolution
1217
Debug|Any CPU = Debug|Any CPU

StringExtract.cs

Lines changed: 82 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,27 +19,64 @@ public static class StringExtract
1919
private const int CHARACTER_AFTER_MARKER = 1;
2020
private const int BACKSET_FOR_ZEROBASED = -1;
2121

22+
/// <summary>
23+
///
24+
/// IsNullWhiteSpaceOrEmpty - Simple expanding name to avoid constant confusion I have with what this function does.
25+
/// Because most people don't equivocate white space with an empty space, which is the opposite of space. It is non-space.
26+
///
27+
/// </summary>
28+
/// <param name="input"></param>
29+
/// <returns></returns>
30+
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
31+
public static bool IsNullOrWhiteSpaceOrEmpty(string input)
32+
{
33+
return (string.IsNullOrWhiteSpace(input));
34+
}
35+
36+
/// <summary>
37+
///
38+
/// LeftOf - Find the marker, and pull the entire string before that marker first appears, not including that marker.
39+
///
40+
/// </summary>
41+
/// <param name="input">input possibly containing the marker, at least one instance of that marker.</param>
42+
/// <param name="marker">the string to find.</param>
43+
/// <usecase>null or empty string as a marker returns that input. Think of use case: Running column values through a "LeftOf(',')" Null input should not magically
44+
/// change into a value, i.e., a set of blanks or empty string! By returning input, we say, "Nevermind, just pass thru."</usecase>
45+
/// <returns></returns>
2246
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
2347
public static string LeftOf(string input, string marker)
2448
{
25-
if (string.IsNullOrEmpty(input)) return null;
49+
if (IsNullOrWhiteSpaceOrEmpty(input)) return input;
50+
if (marker == null) return null; // The test is invalid! null is NOT a valid search value, so the result must represent INVALID parameter!
2651

2752
var i = input.IndexOf(marker);
28-
if (i == NOT_FOUND) return null;
53+
if (i == NOT_FOUND) return string.Empty; // Why? So, "LeftOf('x', 'y') is '', because empty string represents NOTHING, where as null represents INVALID INPUTS. This may help chaining functions. A null from a valid test will force any expression to null. Is that what is desired?
54+
2955
return input.Substring(0, i);
3056
}
3157
private static int IndexOfLastChar(this String str)
3258
{
33-
if (string.IsNullOrWhiteSpace(str)) return NOT_FOUND;
59+
if (IsNullOrWhiteSpaceOrEmpty(str)) return NOT_FOUND;
3460
return str.Length - 1;
3561
}
3662

63+
/// <summary>
64+
///
65+
/// LeftOfNth - Find anything left of the nth found marker - TO THE PREVIOUS MARKER AND NOT INCLUDING THE PREVIOUS MARKER
66+
///
67+
/// </summary>
68+
/// <param name="input"></param>
69+
/// <param name="marker"></param>
70+
/// <param name="n"></param>
71+
/// <flaws>May need a better name. LeftOfNthFromPrevious?</flaws>
72+
/// <example>LeftOfNth("EDWPROD.UserData.x.y", ".", 2);=> UserData</example>
73+
/// <example>LeftOfNth("EDWPROD.UserData.x.y", ".", 3);=> x</example>
74+
/// <returns></returns>
3775
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
3876
public static string LeftOfNth(string input, string marker, int n)
3977
{
40-
if (string.IsNullOrEmpty(input)) return null;
41-
if (n < 0) throw new ArgumentOutOfRangeException(nameof(n));
42-
if (n == 0) return null;
78+
if (string.IsNullOrEmpty(input)) return input;
79+
if (n <= 0) throw new ArgumentOutOfRangeException(nameof(n));
4380
var i = input.IndexOf(marker);
4481
if (i == NOT_FOUND) return null;
4582
if (n == 1) return input.Substring(0, i);
@@ -49,14 +86,29 @@ public static string LeftOfNth(string input, string marker, int n)
4986
if (i == input.IndexOfLastChar()) return string.Empty;
5087
previous_i = i;
5188
i = input.IndexOf(marker, i + marker.Length);
52-
if (i == NOT_FOUND) return null;
89+
if (i == NOT_FOUND) return string.Empty;
5390
}
5491
if (i >= input.IndexOfLastChar()) return string.Empty;
5592
if (i == NOT_FOUND) return null;
5693
int seglen = i - (previous_i + marker.Length);
5794
return input.Substring(previous_i + marker.Length, seglen);
5895
}
5996

97+
/// <summary>
98+
///
99+
/// LeftMOfNth - Pull m pieces back from the nth finding of a marker.
100+
///
101+
/// </summary>
102+
/// <param name="input"></param>
103+
/// <param name="marker"></param>
104+
/// <param name="n"></param>
105+
/// <param name="howmany"></param>
106+
/// <example>LeftMOfNth("EDWPROD.UserData.x.y", ".", 1, 1);=> EDWPROD</example>
107+
/// <example>LeftMOfNth("EDWPROD.UserData.x.y", ".", 2, 2);=> EDWPROD.UserData</example>
108+
/// <example>LeftMOfNth("EDWPROD.UserData.x.y", ".", 1, 2);=> </example>
109+
/// <example>LeftMOfNth("EDWPROD.UserData.x.y", ".", 2, 4);=> </example>
110+
/// <bug>LeftMOfNth("..", ".", 1, 1);=> .</bug>
111+
/// <returns></returns>
60112
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
61113
public static string LeftMOfNth(string input, string marker, int n, int howmany)
62114
{
@@ -71,7 +123,7 @@ public static string LeftMOfNth(string input, string marker, int n, int howmany)
71123
if (i >= input.IndexOfLastChar()) return string.Empty;
72124
pointsfound[j+BACKSET_FOR_ZEROBASED] = i;
73125
i = input.IndexOf(marker, i + marker.Length);
74-
if (i == NOT_FOUND) return null;
126+
if (i == NOT_FOUND) return string.Empty;
75127
if (j == howmany)
76128
{
77129
int startofseg = pointsfound[j - howmany];
@@ -83,33 +135,41 @@ public static string LeftMOfNth(string input, string marker, int n, int howmany)
83135
return string.Empty;
84136
}
85137

138+
/// <summary>
139+
///
140+
/// LeftOfAny - Any of the characters in marker, any string before any of those.
141+
///
142+
/// </summary>
143+
/// <param name="input"></param>
144+
/// <param name="markerchars"></param>
145+
/// <returns></returns>
86146
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
87-
public static string LeftOfAny(string input, string markers)
147+
public static string LeftOfAny(string input, string markerchars)
88148
{
89-
if (string.IsNullOrEmpty(input)) return null;
149+
if (IsNullOrWhiteSpaceOrEmpty(input)) return input;
90150

91-
var i = input.IndexOfAny(markers.ToCharArray());
92-
if (i == NOT_FOUND) return null;
151+
var i = input.IndexOfAny(markerchars.ToCharArray());
152+
if (i == NOT_FOUND) return string.Empty;
93153
return input.Substring(0, i);
94154
}
95155

96156
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
97157
public static string RightOf(string input, string marker)
98158
{
99-
if (string.IsNullOrEmpty(input)) return null;
159+
if (string.IsNullOrEmpty(input)) return input;
100160

101161
var i = input.IndexOf(marker);
102-
if (i == NOT_FOUND) return null;
162+
if (i == NOT_FOUND) return string.Empty;
103163
return input.Substring(i + marker.Length);
104164
}
105165

106166
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
107167
public static string RightOfAny(string input, string markers)
108168
{
109-
if (string.IsNullOrEmpty(input)) return null;
169+
if (IsNullOrWhiteSpaceOrEmpty(input)) return input;
110170

111171
var i = input.IndexOfAny(markers.ToCharArray());
112-
if (i == NOT_FOUND) return null;
172+
if (i == NOT_FOUND) return string.Empty;
113173
return input.Substring(i + 1);
114174
}
115175

@@ -121,9 +181,11 @@ public static string RightOfAny(string input, string markers)
121181
/// <param name="FullName"></param>
122182
/// <returns></returns>
123183
[SqlFunction(DataAccess = DataAccessKind.None, IsDeterministic = true, IsPrecise = true)]
124-
public static SqlString GetFirstName(SqlString FullName)
184+
public static string GetFirstName(string FullName)
125185
{
126-
string workingFullName = FullName.ToString().Trim();
186+
if (IsNullOrWhiteSpaceOrEmpty(FullName)) return FullName;
187+
188+
string workingFullName = FullName.Trim();
127189
string firstName;
128190

129191
if (workingFullName.EndsWith("(CWF)")) workingFullName = workingFullName.Substring(0, workingFullName.Length - 6).Trim();
@@ -148,7 +210,8 @@ public static SqlString GetFirstName(SqlString FullName)
148210
{
149211
firstName = firstName.Substring(0, firstName.Length - 2);
150212
}
151-
return new SqlString(firstName.Trim());
213+
214+
return firstName.Trim();
152215
}
153216
}
154217
}

TestMySQLCLRFunctions/Program.cs

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,39 +12,63 @@ class Program
1212
{
1313
static void Main(string[] args)
1414
{
15-
string output; string input; string marker; int markerno; int howmanyback;
16-
// var i = MySQLCLRFunctions.StringExtract.LeftOf("High.There", ".");
17-
// Debug.Print(i);
18-
// i = MySQLCLRFunctions.StringExtract.LeftOfNth("High.There", ".", 2);
19-
// Debug.Print(i);
20-
input = "EDWPROD.UserData.x.y";
21-
marker = ".";
22-
markerno = 2;
23-
howmanyback = 1;
24-
output = MySQLCLRFunctions.StringExtract.LeftOfNth(input, marker, markerno);
25-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfNth(\"{input}\", \"{marker}\", {markerno});=> {output}");
15+
string output; string input; string marker; string markerchars; string markers; int markerno; int howmanyback;
16+
17+
input = "EDWPROD.UserData.x.y"; marker = "."; markerno = 2; howmanyback = 1;
18+
output = MySQLCLRFunctions.StringExtract.LeftOfNth(input, marker, markerno); if (output == null) output = "{null}";
19+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfNth(\"{input}\", \"{marker}\", {markerno});=>{output}<=");
20+
2621
markerno = 3;
27-
output = MySQLCLRFunctions.StringExtract.LeftOfNth(input, marker, markerno);
28-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfNth(\"{input}\", \"{marker}\", {markerno});=> {output}");
22+
output = MySQLCLRFunctions.StringExtract.LeftOfNth(input, marker, markerno); if (output == null) output = "{null}";
23+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfNth(\"{input}\", \"{marker}\", {markerno});=>{output}<=");
24+
2925
markerno = 5;
30-
output = MySQLCLRFunctions.StringExtract.LeftOfNth(input, marker, markerno);
31-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfNth(\"{input}\", \"{marker}\", {markerno});=> {output}");
26+
output = MySQLCLRFunctions.StringExtract.LeftOfNth(input, marker, markerno); if (output == null) output = "{null}";
27+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfNth(\"{input}\", \"{marker}\", {markerno});=>{output}<=");
3228

3329
markerno = 1;
34-
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback);
35-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=> {output}");
30+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
31+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
32+
3633
markerno = 2; howmanyback = 2;
37-
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback);
38-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=> {output}");
34+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
35+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
36+
3937
markerno = 1; howmanyback = 2;
40-
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback);
41-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=> {output}");
38+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
39+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
40+
4241
markerno = 2; howmanyback = 4;
43-
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback);
44-
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=> {output}");
42+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
43+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
44+
45+
markerno = 1; howmanyback = 1; input = ".."; marker = ".";
46+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
47+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
48+
49+
markerno = 1; howmanyback = 1; input = ".."; marker = "..";
50+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
51+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
52+
53+
markerno = 2; howmanyback = 1; input = ".."; marker = ".";
54+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
55+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
56+
57+
markerno = 2; howmanyback = 1; input = ".."; marker = ".";
58+
output = MySQLCLRFunctions.StringExtract.LeftMOfNth(input, marker, markerno, howmanyback); if (output == null) output = "{null}";
59+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftMOfNth(\"{input}\", \"{marker}\", {markerno}, {howmanyback});=>{output}<=");
60+
61+
input = "He.was;therefore:Not."; markerchars = ".,;:";
62+
output = MySQLCLRFunctions.StringExtract.LeftOfAny(input, markerchars); if (output == null) output = "{null}";
63+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfAny(\"{input}\", \"{markerchars}\";=>{output}<=");
64+
65+
input = "He.was;therefore:Not."; markerchars = ";:.,";
66+
output = MySQLCLRFunctions.StringExtract.LeftOfAny(input, markerchars); if (output == null) output = "{null}";
67+
Debug.Print($"MySQLCLRFunctions.StringExtract.LeftOfAny(\"{input}\", \"{markerchars}\";=>{output}<=");
68+
4569
input = "[xxx]]y]";
46-
output = MySQLCLRFunctions.StringTransform.RemoveSQLServerNameDelimiters($"{input}");
47-
Debug.Print($"MySQLCLRFunctions.StringTransform.RemoveSQLServerNameDelimiters(\"{input}\");=> {output}");
70+
output = MySQLCLRFunctions.StringTransform.RemoveSQLServerNameDelimiters($"{input}"); if (output == null) output = "{null}";
71+
Debug.Print($"MySQLCLRFunctions.StringTransform.RemoveSQLServerNameDelimiters(\"{input}\");=>{output}<=");
4872
}
4973
}
5074
}

0 commit comments

Comments
 (0)