Last active
August 29, 2015 14:14
-
-
Save neremin/c5b967ed0fd8bf38958f to your computer and use it in GitHub Desktop.
This file contains 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
/// <devdoc> | |
/// Useful in number of places that return an empty byte array to avoid unnecessary memory allocation. | |
/// Borrowed from .NET Framework's Array.cs. | |
/// </devdoc> | |
public static class EmptyArray<T> | |
{ | |
public static readonly T[] Value = new T[0]; | |
} |
This file contains 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
public static class StringExtensions | |
{ | |
static readonly WeakLazy<char[]> InvalidFileNameChars = new WeakLazy<char[]>(Path.GetInvalidFileNameChars); | |
static readonly WeakLazy<char[]> InvalidPathChars = new WeakLazy<char[]>(Path.GetInvalidPathChars); | |
[Pure] | |
public static bool IsValidFileName(this string name) | |
{ | |
return !string.IsNullOrEmpty(name) && name.IndexOfAny(InvalidFileNameChars.Value) == -1; | |
} | |
[Pure] | |
public static bool IsValidFilePath(this string name) | |
{ | |
return !string.IsNullOrEmpty(name) && name.IndexOfAny(InvalidPathChars.Value) == -1; | |
} | |
public static string ReplaceInvalidFileNameChars(this string name, string replacement = null) | |
{ | |
return Replace(name, replacement, InvalidFileNameChars.Value); | |
} | |
public static string ReplaceInvalidFilePathChars(this string name, string replacement = null) | |
{ | |
return Replace(name, replacement, InvalidPathChars.Value); | |
} | |
public static string Replace(this string value, string replacement, params char[] replacedChars) | |
{ | |
if (string.IsNullOrEmpty(value) || replacedChars == null) | |
{ | |
return value; | |
} | |
var start = 0; | |
var length = value.Length; | |
var position = -1; | |
var result = new StringBuilder(length); | |
for (; start < length && (position = value.IndexOfAny(replacedChars, start)) != -1; start = position + 1) | |
{ | |
result.Append(value, start, position - start); | |
result.Append(replacement); | |
} | |
result.Append(value, start, Math.Max(position, length) - start); | |
return result.ToString(); | |
} | |
public static IEnumerable<T> SplitAndConvert<T>(this string value, string separator, | |
Converter<string, T> converter, bool skipEmpty = true) | |
{ | |
return string.IsNullOrEmpty(value) | |
? EmptyArray<T>.Value | |
: value.Split | |
( | |
new[] { separator }, | |
(skipEmpty ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None) | |
) | |
.Select(element => converter(element)); | |
} | |
public static IEnumerable<string> Split(this string value, string separator, bool skipEmpty = true) | |
{ | |
Contract.Requires<ArgumentNullException>(value != null); | |
if (value.Length == 0) | |
{ | |
return EmptyArray<string>.Value; | |
} | |
var options = (skipEmpty ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None); | |
return value.Split(new[] {separator}, options); | |
} | |
public static IEnumerable<T> SplitAndConvert<T>(this string value, string separator, | |
Converter<string, T> converter, bool skipEmpty = true) | |
{ | |
Contract.Requires<ArgumentNullException>(converter != null); | |
return value.Split(separator, skipEmpty).Select(element => converter(element)); | |
} | |
public static string AppendLine(this string firstLine, string line) | |
{ | |
return string.Concat(firstLine, Environment.NewLine, line); | |
} | |
public static string AppendLines(this string firstLine, IEnumerable<string> lines) | |
{ | |
Contract.Requires<ArgumentNullException>(firstLine != null); | |
Contract.Requires<ArgumentNullException>(lines != null); | |
return lines.Prepend(firstLine).JoinLines(); // Requires LinqExtensions.cs | |
} | |
public static string Args(this string format, params object[] args) | |
{ | |
return string.Format(format, args); | |
} | |
} |
This file contains 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
[TestFixture] | |
public class StringExtensionsTests | |
{ | |
[TestCase(null, "9", "*", Result = null)] | |
[TestCase("*", null, "*", Result = "")] | |
[TestCase("*", "", "*", Result = "")] | |
[TestCase("", "9", "*", Result = "")] | |
[TestCase("*", "9", "*", Result = "9")] | |
[TestCase("*", "9", null, Result = "*")] | |
[TestCase("*-*-*", "99", "*", Result = "99-99-99")] | |
[TestCase("-*-*-*-", "99", "*", Result = "-99-99-99-")] | |
[TestCase("________", "9", "*", Result = "________")] | |
[TestCase("_*__*___**_*", "9", "*", Result = "_9__9___99_9")] | |
[TestCase("*__*__**_*___", "9", "*", Result = "9__9__99_9___")] | |
public string Replace(string input, string replacement, string replacedChars) | |
{ | |
return input.Replace(replacement, replacedChars == null ? null : replacedChars.ToCharArray()); | |
} | |
[TestCase("_1__23__4_5", "*", "1234567890", 1000)] | |
public void Replace_Performance(string input, string replacement, string replacedChars, int iterations) | |
{ | |
var value = Enumerable.Repeat(input, 10000).ConcatStrings(); | |
var chars = replacedChars.ToCharArray(); | |
Console.WriteLine("Length: " + value.Length); | |
Console.WriteLine("Cardinality: " + value.Length * replacedChars.Length); | |
GC.Collect(3, GCCollectionMode.Forced); | |
var stopwatch = Stopwatch.StartNew(); | |
for (int i = 0; i < iterations; ++i) | |
{ | |
value.Replace(replacement, chars); | |
} | |
Console.WriteLine("Replace: " + stopwatch.Elapsed); | |
GC.Collect(3, GCCollectionMode.Forced); | |
stopwatch.Restart(); | |
for (int i = 0; i < iterations; ++i) | |
{ | |
string.Join(replacement, value.Split(chars, StringSplitOptions.None)); | |
} | |
Console.WriteLine("Split & Join: " + stopwatch.Elapsed); | |
var charCodes = string.Concat(chars.Select(c => @"\u" + ((short)c).ToString("x4"))); | |
var charsPattern = @"[" + charCodes + "]"; | |
var regex = new Regex(charsPattern, RegexOptions.Compiled); | |
GC.Collect(3, GCCollectionMode.Forced); | |
stopwatch.Restart(); | |
for (int i = 0; i < iterations; ++i) | |
{ | |
regex.Replace(value, replacement); | |
} | |
Console.WriteLine("Regex: " + stopwatch.Elapsed); | |
} | |
[TestCase(null, ",", true, ";", ExpectedException = typeof(ArgumentNullException))] | |
[TestCase("1234", null, true, ";", Result = "1234")] | |
[TestCase("1234", "", true, ";", Result = "1234")] | |
[TestCase(",1,2,3,4,5,", ",", true, ";", Result = "1;2;3;4;5")] | |
[TestCase(",1,2,3,4,5,", ",", false, ";", Result = ";1;2;3;4;5;")] | |
public string Split(string value, string separator, bool skipEmpty, string join) | |
{ | |
var result = value.Split(separator, skipEmpty); | |
return result.Join(join); | |
} | |
[TestCase(",1,2,3,4,5,", ",", "*", true, ";", Result = "*;*;*;*;*")] | |
[TestCase(",1,2,3,4,5,", ",", "*", false, ";", Result = "*;*;*;*;*;*;*")] | |
public string SplitAndConvert(string value, string separator, string replacement, bool skipEmpty, string join) | |
{ | |
var result = value.SplitAndConvert(separator, s => replacement, skipEmpty); | |
return result == null ? null : result.Join(join); | |
} | |
} |
This file contains 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
public sealed class WeakLazy<T> where T: class | |
{ | |
readonly WeakReference<T> reference; | |
readonly Func<T> valueFactory; | |
public WeakLazy(Func<T> valueFactory) | |
{ | |
Contract.Requires<ArgumentNullException>(valueFactory != null); | |
reference = new WeakReference<T>(null); | |
this.valueFactory = valueFactory; | |
} | |
public T Value | |
{ | |
get | |
{ | |
T value; | |
if (!reference.TryGetTarget(out value)) | |
{ | |
value = valueFactory(); | |
reference.SetTarget(value); | |
} | |
return value; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment