diff --git a/src/ByteDev.Strings/StringExtensions.cs b/src/ByteDev.Strings/StringExtensions.cs index 0a79686..1b8d9e2 100644 --- a/src/ByteDev.Strings/StringExtensions.cs +++ b/src/ByteDev.Strings/StringExtensions.cs @@ -223,5 +223,35 @@ public static string InsertBeforeUpperCase(this string source, string delimiter) return sb.ToString(); } + + /// + /// Extracts the string between the specified start and end string. + /// e.g. for "The quick brown fox jumps over the lazy dog", the string between "quick" and "jumps" is " brown fox ". + /// If there are multiple instances of a start or end string, it will use the first instance. + /// An empty string will be returned if no matches were found. + /// + /// String to perform the operation on. + /// The start string to find + /// The end string to find after the startString + /// The string comparison to use (defaults to StringComparison.OrdinalIgnoreCase) + /// The string in between startString and endString, or empty string if not found + public static string ExtractStringBetween(this string source, string startString, string endString, StringComparison stringComparison = StringComparison.OrdinalIgnoreCase) + { + if (string.IsNullOrEmpty(source) || string.IsNullOrEmpty(startString) || string.IsNullOrEmpty(endString)) + return string.Empty; + + var extractedString = string.Empty; + var startIndex = source.IndexOf(startString, stringComparison); + + if (startIndex > -1) + { + startIndex += startString.Length; + var endIndex = source.IndexOf(endString, startIndex, stringComparison); + if (endIndex > -1) + extractedString = source.SafeSubstring(startIndex, endIndex - startIndex); + } + + return extractedString; + } } } \ No newline at end of file diff --git a/src/ByteDev.Strings/StringToExtensions.cs b/src/ByteDev.Strings/StringToExtensions.cs index 65fa888..f10a97e 100644 --- a/src/ByteDev.Strings/StringToExtensions.cs +++ b/src/ByteDev.Strings/StringToExtensions.cs @@ -320,5 +320,31 @@ public static MemoryStream ToMemoryStream(this string source, Encoding encoding) return new MemoryStream(bytes); } + + /// + /// Returns the string as a sequence with each value determined by the specified char delimiter, and converted to the specified type. + /// + /// The type to convert each item into. + /// The string to perform this operation on. + /// Value delimiter. + /// True trim each value; false do nothing. + /// Collection of values; otherwise empty. Throws exception if values cannot be converted. + public static IEnumerable ToConvertedSequence(this string sourceString, char delimiter = ',', bool trimValues = false) + where T : struct, IConvertible + { + if (string.IsNullOrEmpty(sourceString)) + return Enumerable.Empty(); + + try + { + IEnumerable stringValues = sourceString.ToSequence(delimiter, trimValues); + + return stringValues.Select(stringValue => (T)Convert.ChangeType(stringValue, typeof(T))); + } + catch (Exception) + { + return Enumerable.Empty(); + } + } } } \ No newline at end of file diff --git a/tests/ByteDev.Strings.UnitTests/StringExtensionsTests.cs b/tests/ByteDev.Strings.UnitTests/StringExtensionsTests.cs index 29781ff..501a0e3 100644 --- a/tests/ByteDev.Strings.UnitTests/StringExtensionsTests.cs +++ b/tests/ByteDev.Strings.UnitTests/StringExtensionsTests.cs @@ -561,5 +561,49 @@ public void WhenHasUpperCase_ThenReturnString(string sut, string expected) Assert.That(result, Is.EqualTo(expected)); } } + + [TestFixture] + public class ExtractStringBetween + { + [TestCase(null)] + [TestCase("")] + public void WhenSourceIsNullOrEmpty_ThenReturnEmptyString(string source) + { + var result = source.ExtractStringBetween("test1", "test2"); + + Assert.That(result, Is.EqualTo(string.Empty)); + } + + [TestCase("The quick brown fox jumps over the lazy dog", "quick1", "jumps")] + [TestCase("The quick brown fox jumps over the lazy dog", "the", "not found")] + public void WhenIsNotAMatchingSearch_ThenReturnEmptyString(string source, string startString, string endString) + { + var result = source.ExtractStringBetween(startString, endString); + + Assert.That(result, Is.EqualTo(string.Empty)); + } + + [TestCase("The quick brown fox jumps over the lazy dog", "quick", "jumps", " brown fox ")] + [TestCase("The quick brown fox jumps over the lazy dog", "the", "dog", " quick brown fox jumps over the lazy ")] + [TestCase("The quick brown fox jumps over the lazy dog", "lazy", "dog", " ")] + [TestCase("My test string", "", "", "My test string")] + [TestCase("My test string", "(); + + Assert.IsNotNull(result); + Assert.That(result.Count(), Is.EqualTo(0)); + } + + [TestCase("1")] + [TestCase("489")] + [TestCase("2147483647")] + public void WhenDoesNotContainDelimiter_ThenReturnListWithOneValue(string sut) + { + var result = sut.ToConvertedSequence(); + + Assert.IsNotNull(result); + var resultAsList = result.ToList(); + Assert.That(resultAsList.Count, Is.EqualTo(1)); + Assert.That(resultAsList[0], Is.EqualTo(Convert.ToInt32(sut))); + } + + [TestCase("1,444,44", ',')] + [TestCase("89,092,7,9", ',')] + [TestCase("0:7", ':')] + [TestCase("8?99?5432?900", '?')] + public void WhenContainsDelimiter_ThenReturnListWithValues(string sut, char delimiter) + { + var expected = sut.Split(delimiter).Select(s => Convert.ToInt64(s)); + var result = sut.ToConvertedSequence(delimiter); + + Assert.IsNotNull(result); + Assert.That(result, Is.EqualTo(expected)); + } + + [TestCase("abc", ',')] + [TestCase("1,444,44", ':')] + [TestCase("0:7", ',')] + public void WhenInvalidValues_ThenThrowException(string sut, char delimiter) + { + Assert.Throws(() => sut.ToConvertedSequence(delimiter).ToList(), "Input string was not in a correct format."); + } + } } } \ No newline at end of file