Skip to content

Commit ec744d4

Browse files
Added more StringSegment methods.
1 parent 5cfaf31 commit ec744d4

4 files changed

Lines changed: 95 additions & 1 deletion

File tree

Source/Extensions.StringSegment.cs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Diagnostics.Contracts;
3+
using System.Text.RegularExpressions;
34

45
namespace Open.Text
56
{
@@ -44,6 +45,26 @@ public static StringSegment First(this string source, string search, StringCompa
4445
return i == -1 ? default : StringSegment.Create(source, i, search.Length);
4546
}
4647

48+
/// <summary>
49+
/// Finds the first instance of a pattern and returns a StringSegment for subsequent use.
50+
/// </summary>
51+
/// <param name="source">The source string to search.</param>
52+
/// <param name="pattern">The pattern to look for.</param>
53+
/// <returns>
54+
/// The segment representing the found string.
55+
/// If not found, the StringSegment.IsValid property will be false.
56+
/// </returns>
57+
/// <remarks>If the pattern is right-to-left, then it will return the first segment from the right.</remarks>
58+
public static StringSegment First(this string source, Regex pattern)
59+
{
60+
if (source is null) throw new ArgumentNullException(nameof(source));
61+
if (pattern is null) throw new ArgumentNullException(nameof(pattern));
62+
Contract.EndContractBlock();
63+
64+
var match = pattern.Match(source);
65+
return match.Success ? StringSegment.Create(source, match.Index, match.Length) : default;
66+
}
67+
4768
/// <inheritdoc cref="First(string, string, StringComparison)" />
4869
public static StringSegment First(this StringSegment source, in ReadOnlySpan<char> search, StringComparison comparisonType = StringComparison.Ordinal)
4970
{
@@ -83,6 +104,23 @@ public static StringSegment Last(this string source, string search, StringCompar
83104
return i == -1 ? default : StringSegment.Create(source, i, search.Length);
84105
}
85106

107+
/// <inheritdoc cref="First(string, Regex)"/>
108+
/// <summary>
109+
/// Finds the last instance of a pattern and returns a StringSegment for subsequent use.
110+
/// </summary>
111+
/// <remarks>If the pattern is right-to-left, then it will return the last segment from the right (first segment from the left).</remarks>
112+
public static StringSegment Last(this string source, Regex pattern)
113+
{
114+
if (source is null) throw new ArgumentNullException(nameof(source));
115+
if (pattern is null) throw new ArgumentNullException(nameof(pattern));
116+
Contract.EndContractBlock();
117+
118+
var matches = pattern.Matches(source);
119+
if (matches.Count == 0) return default;
120+
var match = matches[matches.Count - 1];
121+
return StringSegment.Create(source, match.Index, match.Length);
122+
}
123+
86124
/// <inheritdoc cref="Last(string, string, StringComparison)" />
87125
public static StringSegment Last(this StringSegment source, in ReadOnlySpan<char> search, StringComparison comparisonType = StringComparison.Ordinal)
88126
{

Source/Open.Text.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<RepositoryUrl>https://github.com/Open-NET-Libraries/Open.Text</RepositoryUrl>
1818
<RepositoryType>git</RepositoryType>
1919
<PackageTags>dotnet, dotnetcore, string, span, readonlyspan, text, format, split, trim, first, last, preceding, following, stringbuilder, extensions</PackageTags>
20-
<Version>3.0.1</Version>
20+
<Version>3.1.0</Version>
2121
<PackageReleaseNotes></PackageReleaseNotes>
2222
<PackageLicenseExpression>MIT</PackageLicenseExpression>
2323
<PublishRepositoryUrl>true</PublishRepositoryUrl>

Source/StringSegment.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,50 @@ public StringSegment Following(int maxCharacters)
8484
return new(Source, start, Math.Min(maxCharacters, len - start));
8585
}
8686

87+
/// <summary>
88+
/// Creates a StringSegment that starts offset by the value provided.
89+
/// </summary>
90+
/// <param name="offset">The value (positive or negative) to move the index by and adjust the length.</param>
91+
public StringSegment OffsetIndex(int offset)
92+
{
93+
if (!IsValid) return default;
94+
var newIndex = Index + offset;
95+
if (newIndex < 0) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Cannot index less than the start of the string.");
96+
if (offset>Length) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Cannot index greater than the length of the segment.");
97+
return new(Source, newIndex, Length - offset);
98+
}
99+
100+
/// <summary>
101+
/// Creates a StringSegment modifies the length by the value provided.
102+
/// </summary>
103+
/// <param name="offset">The value (positive or negative) to adjust the length.</param>
104+
public StringSegment OffsetLength(int offset)
105+
{
106+
if (!IsValid) return default;
107+
var newLength = Length + offset;
108+
if (newLength < 0) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Cannot shrink less than the start of the string.");
109+
if (Index + newLength > Source.Length) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Cannot expand greater than the length of the source.");
110+
return new(Source, Index, newLength);
111+
}
112+
113+
/// <summary>
114+
/// Creates a StringSegment that starts offset by the value provided and extends by the length provided.
115+
/// </summary>
116+
/// <param name="offset">The value (positive or negative) to move the index by.</param>
117+
/// <param name="length">The length desired.</param>
118+
/// <param name="ignoreLengthBoundary">
119+
/// If true, the length can exceed the segment length but not past the full length of the source string.
120+
/// If false (default), an ArgumentOutOfRangeException will be thrown if the expected length exeeds the segment.
121+
/// </param>
122+
public StringSegment Slice(int offset, int length)
123+
{
124+
if (!IsValid) return default;
125+
var newIndex = Index + offset;
126+
if (Index + offset < 0) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Cannot index less than the start of the string.");
127+
if (offset > Length) throw new ArgumentOutOfRangeException(nameof(offset), offset, "Cannot index greater than the length of the segment.");
128+
return new(Source, newIndex, Length - offset);
129+
}
130+
87131
/// <summary>
88132
/// Returns true if the other string segment values match.
89133
/// </summary>

Tests/BeforeAfterTests.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,5 +56,17 @@ public static void AfterLast(string source, string pattern, string? expected, St
5656
if (expected is null) Assert.False(last.IsValid);
5757
else Assert.Equal(expected, last.Following().ToString());
5858
}
59+
60+
[Fact]
61+
public static void Offsets()
62+
{
63+
var segment = "Hello well how are you".First("well how");
64+
Assert.Equal("well", segment.OffsetLength(-4).ToString());
65+
Assert.Equal("how", segment.OffsetIndex(+5).ToString());
66+
Assert.Equal("well how are", segment.OffsetLength(+4).ToString());
67+
Assert.Equal("Hello well how", segment.OffsetIndex(-6).ToString());
68+
Assert.Throws<ArgumentOutOfRangeException>(() => segment.OffsetIndex(-7));
69+
Assert.Throws<ArgumentOutOfRangeException>(() => segment.OffsetLength(9));
70+
}
5971
}
6072
}

0 commit comments

Comments
 (0)