Last active
January 26, 2017 20:40
-
-
Save JasonKleban/ab74920c41e805f5696117d7aa0a03d8 to your computer and use it in GitHub Desktop.
Pack/Unpack in C# - string array serialization compatible with the t-sql implementations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static class PackUtil | |
{ | |
public static string Pack(string[] original) | |
{ | |
return Pack(original, '|', '0', '~'); | |
} | |
public static string[] Unpack(string original) | |
{ | |
return Unpack(original, '|', '0', '~'); | |
} | |
public static string Pack(string[] original, char delimiter, char zed, char escape) | |
{ | |
if (delimiter == escape || | |
zed == escape || | |
delimiter == zed) throw new ArgumentException("special characters must be distinct"); | |
// Null array returns a null string | |
if (original == null) return null; | |
// Empty array returns an empty string | |
if (original.Length == 0) return string.Empty; | |
// Arrays with a single empty element are represented as just the escape character | |
// to differentiate from an empty array | |
if (original.Length == 1 && original[0] == string.Empty) return escape.ToString(); | |
// Otherwise | |
StringBuilder sb = new StringBuilder(); | |
for (int i = 0, ol = original.Length; i < ol; i++) | |
{ | |
string s = original[i]; | |
if (s == null) | |
{ | |
sb.Append(zed); // zed == null | |
} | |
else | |
{ | |
for (int j = 0, sl = s.Length; j < sl; j++) | |
{ | |
char c = s[j]; | |
// escape literal delimiters, escapes, and leading zeds | |
if (c == delimiter || | |
c == escape || | |
(c == zed && j == 0)) sb.Append(escape); | |
sb.Append(c); | |
} | |
} | |
if (i != ol - 1) sb.Append(delimiter); // no trailing delimiter | |
} | |
return sb.ToString(); | |
} | |
public static string[] Unpack(string original, char delimiter, char zed, char escape) | |
{ | |
if (delimiter == escape || | |
zed == escape || | |
delimiter == zed) throw new ArgumentException("special characters must be distinct"); | |
// Null string returns a null array | |
if (original == null) return null; | |
// Empty string returns an empty array | |
if (original == string.Empty) return new string[] { }; | |
// A single escape character represents an array with a single empty element | |
// to differentiate from an empty array | |
if (original == escape.ToString()) return new string[] { string.Empty }; | |
// Otherwise | |
StringBuilder sb = new StringBuilder(); // A place to store the current element | |
StringReader sr = new StringReader(original); // A stream of the original string | |
List<string> unpacked = new List<string>(); // The finished elements | |
int next; | |
while ((next = sr.Read()) >= 0) | |
{ | |
char c = (char)next; | |
if (c == zed && sb.Length == 0) | |
{ | |
if ((next = sr.Peek()) >= 0 && (char)next != delimiter) | |
throw new ArgumentException("An element's leading zed character must be escaped or must alone be the element", "original"); | |
sb = null; | |
} | |
else if (c == delimiter) | |
{ | |
unpacked.Add(sb?.ToString()); | |
sb = new StringBuilder(); | |
} | |
else if (c == escape) | |
{ | |
if ((next = sr.Read()) >= 0) | |
{ | |
sb.Append((char)next); | |
} | |
else | |
throw new ArgumentException("Escapee expected", "original"); | |
} | |
else | |
{ | |
sb.Append(c); | |
} | |
} | |
unpacked.Add(sb?.ToString()); | |
return unpacked.ToArray(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment