@@ -42,13 +42,78 @@ public static class StringExtensions
42
42
/// </summary>
43
43
private const DateTimeStyles DefaultStyles = DateTimeStyles . None ;
44
44
45
+ /// <summary>
46
+ /// Obtains the zero-based index of the nth occurrence of the specified character in this instance.
47
+ /// If the specified occurrence does not exist, returns -1.
48
+ /// </summary>
49
+ /// <param name="value">The string to search.</param>
50
+ /// <param name="seekValue">The character to seek.</param>
51
+ /// <param name="count">The number of occurrences to skip before returning an index.</param>
52
+ /// <returns>
53
+ /// Returns the zero-based index position of the nth occurrence of <paramref name="seekValue"/>, if found; otherwise, -1.
54
+ /// </returns>
55
+ public static int NthIndexOf ( this string value , char seekValue , int count )
56
+ {
57
+ if ( string . IsNullOrEmpty ( value ) || count <= 0 ) return - 1 ;
58
+
59
+ int occurrences = 0 ;
60
+
61
+ for ( int i = 0 ; i < value . Length ; i ++ )
62
+ {
63
+ if ( value [ i ] != seekValue ) continue ;
64
+
65
+ occurrences ++ ;
66
+
67
+ if ( occurrences != count ) continue ;
68
+
69
+ return i ;
70
+ }
71
+
72
+ return NotFound ;
73
+ }
74
+
75
+ /// <summary>
76
+ /// Obtains the zero-based index of the nth occurrence of the specified character in this instance.
77
+ /// If the specified occurrence does not exist, returns -1.
78
+ /// </summary>
79
+ /// <param name="value">The string to search.</param>
80
+ /// <param name="seekValue">The substring to seek.</param>
81
+ /// <param name="count">The number of occurrences to skip before returning an index.</param>
82
+ /// <param name="comparison">The comparison that will be used to compare the current value and the seek value.</param>
83
+ /// <returns>
84
+ /// Returns the zero-based index position of the nth occurrence of <paramref name="seekValue"/>, if found; otherwise, -1.
85
+ /// </returns>
86
+ public static int NthIndexOf ( this string value , string seekValue , int count , StringComparison comparison = DefaultComparison )
87
+ {
88
+ if ( string . IsNullOrEmpty ( value ) || string . IsNullOrEmpty ( seekValue ) || count <= 0 ) return - 1 ;
89
+
90
+ int occurrences = 0 ;
91
+ int startIndex = 0 ;
92
+
93
+ while ( true )
94
+ {
95
+ int index = value . IndexOf ( seekValue , startIndex , comparison ) ;
96
+
97
+ if ( index == - 1 ) return - 1 ;
98
+
99
+ occurrences ++ ;
100
+
101
+ if ( occurrences == count ) return index ;
102
+
103
+ startIndex = index + seekValue . Length ;
104
+
105
+ if ( startIndex >= value . Length ) return NotFound ;
106
+ }
107
+ }
108
+
45
109
/// <summary>
46
110
/// Repeats the current <see cref="String"/> by the specified number of repetitions.
47
111
/// </summary>
48
112
/// <param name="value">The <see cref="String"/> instance to repeat.</param>
49
113
/// <param name="count">The number of repetitions of the current <see cref="String"/> instance.</param>
50
114
/// <returns>Returns a new <see cref="String"/> instance representing the repetition of the current <see cref="String"/> instance.</returns>
51
- public static string Repeat ( this string value , int count ) => count > 0 ? string . Join ( string . Empty , Enumerable . Repeat ( value , count ) ) : string . Empty ;
115
+ public static string Repeat ( this string value , int count ) =>
116
+ count > 0 ? string . Join ( string . Empty , Enumerable . Repeat ( value , count ) ) : string . Empty ;
52
117
53
118
/// <summary>
54
119
/// Obtains a sub-string before the specified index within the current <see cref="String"/> instance.
@@ -64,7 +129,8 @@ public static class StringExtensions
64
129
/// If the default value is <see langword="null"/>, then the current <see cref="String"/> instance will be returned.
65
130
/// </returns>
66
131
// ReSharper disable once HeapView.ObjectAllocation
67
- private static string SubstringBeforeIndex ( this string value , int index , string ? defaultValue = null ) => index <= NotFound ? defaultValue ?? value : value [ ..index ] ;
132
+ private static string SubstringBeforeIndex ( this string value , int index , string ? defaultValue = null ) =>
133
+ index <= NotFound ? defaultValue ?? value : value [ ..index ] ;
68
134
69
135
/// <summary>
70
136
/// Obtains a sub-string after the specified index within the current <see cref="String"/> instance.
@@ -81,7 +147,8 @@ public static class StringExtensions
81
147
/// If the default value is <see langword="null"/>, then the current <see cref="String"/> instance will be returned.
82
148
/// </returns>
83
149
// ReSharper disable once HeapView.ObjectAllocation
84
- private static string SubstringAfterIndex ( this string value , int index , int offset , string ? defaultValue = null ) => index <= NotFound ? defaultValue ?? value : value [ ( index + offset ) ..value . Length ] ;
150
+ private static string SubstringAfterIndex ( this string value , int index , int offset , string ? defaultValue = null ) =>
151
+ index <= NotFound ? defaultValue ?? value : value [ ( index + offset ) ..value . Length ] ;
85
152
86
153
/// <summary>
87
154
/// Obtains a sub-string before the first occurrence of the specified delimiter within the current <see cref="String"/> instance.
@@ -235,6 +302,95 @@ public static string SubstringAfterLast(this string value, char delimiter, strin
235
302
public static string SubstringAfterLast ( this string value , string delimiter , string ? defaultValue = null , StringComparison comparison = DefaultComparison ) =>
236
303
value . SubstringAfterIndex ( value . LastIndexOf ( delimiter , comparison ) , 1 , defaultValue ) ;
237
304
305
+ /// <summary>
306
+ /// Obtains a sub-string before the nth occurrence of the specified character within the current <see cref="String"/> instance.
307
+ /// If the nth occurrence is not found, returns the <paramref name="defaultValue"/> or the entire string if default is null.
308
+ /// </summary>
309
+ /// <param name="value">The current <see cref="String"/> instance from which to obtain a sub-string.</param>
310
+ /// <param name="seekValue">The character to find the nth occurrence of.</param>
311
+ /// <param name="count">The nth occurrence to find.</param>
312
+ /// <param name="defaultValue">
313
+ /// The <see cref="String"/> value to return if the nth occurrence is not found.
314
+ /// If the default value is <see langword="null"/>, the current <see cref="String"/> instance is returned.
315
+ /// </param>
316
+ /// <returns>
317
+ /// A sub-string before the nth occurrence of <paramref name="seekValue"/> if found; otherwise,
318
+ /// <paramref name="defaultValue"/> or the entire string if default is null.
319
+ /// </returns>
320
+ public static string SubstringBeforeNth ( this string value , char seekValue , int count , string ? defaultValue = null )
321
+ {
322
+ int index = value . NthIndexOf ( seekValue , count ) ;
323
+ return value . SubstringBeforeIndex ( index , defaultValue ) ;
324
+ }
325
+
326
+ /// <summary>
327
+ /// Obtains a sub-string before the nth occurrence of the specified substring within the current <see cref="String"/> instance.
328
+ /// If the nth occurrence is not found, returns the <paramref name="defaultValue"/> or the entire string if default is null.
329
+ /// </summary>
330
+ /// <param name="value">The current <see cref="String"/> instance from which to obtain a sub-string.</param>
331
+ /// <param name="seekValue">The substring to find the nth occurrence of.</param>
332
+ /// <param name="count">The nth occurrence to find.</param>
333
+ /// <param name="defaultValue">
334
+ /// The <see cref="String"/> value to return if the nth occurrence is not found.
335
+ /// If the default value is <see langword="null"/>, the current <see cref="String"/> instance is returned.
336
+ /// </param>
337
+ /// <param name="comparison">The comparison that will be used to compare the current value and the seek value.</param>
338
+ /// <returns>
339
+ /// A sub-string before the nth occurrence of <paramref name="seekValue"/> if found; otherwise,
340
+ /// <paramref name="defaultValue"/> or the entire string if default is null.
341
+ /// </returns>
342
+ public static string SubstringBeforeNth ( this string value , string seekValue , int count , string ? defaultValue = null , StringComparison comparison = DefaultComparison )
343
+ {
344
+ int index = value . NthIndexOf ( seekValue , count , comparison ) ;
345
+ return value . SubstringBeforeIndex ( index , defaultValue ) ;
346
+ }
347
+
348
+ /// <summary>
349
+ /// Obtains a sub-string after the nth occurrence of the specified character within the current <see cref="String"/> instance.
350
+ /// If the nth occurrence is not found, returns the <paramref name="defaultValue"/> or the entire string if default is null.
351
+ /// </summary>
352
+ /// <param name="value">The current <see cref="String"/> instance from which to obtain a sub-string.</param>
353
+ /// <param name="seekValue">The character to find the nth occurrence of.</param>
354
+ /// <param name="count">The nth occurrence to find.</param>
355
+ /// <param name="defaultValue">
356
+ /// The <see cref="String"/> value to return if the nth occurrence is not found.
357
+ /// If the default value is <see langword="null"/>, the current <see cref="String"/> instance is returned.
358
+ /// </param>
359
+ /// <returns>
360
+ /// A sub-string after the nth occurrence of <paramref name="seekValue"/> if found; otherwise,
361
+ /// <paramref name="defaultValue"/> or the entire string if default is null.
362
+ /// </returns>
363
+ public static string SubstringAfterNth ( this string value , char seekValue , int count , string ? defaultValue = null )
364
+ {
365
+ int index = value . NthIndexOf ( seekValue , count ) ;
366
+ // Move 1 character after the nth occurrence index.
367
+ return value . SubstringAfterIndex ( index , 1 , defaultValue ) ;
368
+ }
369
+
370
+ /// <summary>
371
+ /// Obtains a sub-string after the nth occurrence of the specified substring within the current <see cref="String"/> instance.
372
+ /// If the nth occurrence is not found, returns the <paramref name="defaultValue"/> or the entire string if default is null.
373
+ /// </summary>
374
+ /// <param name="value">The current <see cref="String"/> instance from which to obtain a sub-string.</param>
375
+ /// <param name="seekValue">The substring to find the nth occurrence of.</param>
376
+ /// <param name="count">The nth occurrence to find.</param>
377
+ /// <param name="defaultValue">
378
+ /// The <see cref="String"/> value to return if the nth occurrence is not found.
379
+ /// If the default value is <see langword="null"/>, the current <see cref="String"/> instance is returned.
380
+ /// </param>
381
+ /// <param name="comparison">The comparison that will be used to compare the current value and the seek value.</param>
382
+ /// <returns>
383
+ /// A sub-string after the nth occurrence of <paramref name="seekValue"/> if found; otherwise,
384
+ /// <paramref name="defaultValue"/> or the entire string if default is null.
385
+ /// </returns>
386
+ public static string SubstringAfterNth ( this string value , string seekValue , int count , string ? defaultValue = null , StringComparison comparison = DefaultComparison )
387
+ {
388
+ int index = value . NthIndexOf ( seekValue , count , comparison ) ;
389
+ // Move by the length of the found substring after the nth occurrence index.
390
+ int offset = ( index != NotFound && ! string . IsNullOrEmpty ( seekValue ) ) ? seekValue . Length : 0 ;
391
+ return value . SubstringAfterIndex ( index , offset , defaultValue ) ;
392
+ }
393
+
238
394
/// <summary>
239
395
/// Converts the current <see cref="String"/> instance into a new <see cref="T:Byte[]"/> instance.
240
396
/// </summary>
@@ -336,7 +492,8 @@ public static bool TryCopyTo(this string value, Span<char> destination, out int
336
492
/// <param name="before">The <see cref="Char"/> that should precede the current <see cref="String"/> instance.</param>
337
493
/// <param name="after">The <see cref="Char"/> that should succeed the current <see cref="String"/> instance.</param>
338
494
/// <returns>Returns a new <see cref="String"/> instance representing the current <see cref="String"/> instance, wrapped between the specified before and after <see cref="Char"/> instances.</returns>
339
- public static string Wrap ( this string value , char before , char after ) => string . Concat ( before . ToString ( ) , value , after . ToString ( ) ) ;
495
+ public static string Wrap ( this string value , char before , char after ) =>
496
+ string . Concat ( before . ToString ( ) , value , after . ToString ( ) ) ;
340
497
341
498
/// <summary>
342
499
/// Wraps the current <see cref="String"/> instance between the specified before and after <see cref="String"/> instances.
@@ -345,5 +502,6 @@ public static bool TryCopyTo(this string value, Span<char> destination, out int
345
502
/// <param name="before">The <see cref="String"/> that should precede the current <see cref="String"/> instance.</param>
346
503
/// <param name="after">The <see cref="String"/> that should succeed the current <see cref="String"/> instance.</param>
347
504
/// <returns>Returns a new <see cref="String"/> instance representing the current <see cref="String"/> instance, wrapped between the specified before and after <see cref="String"/> instances.</returns>
348
- public static string Wrap ( this string value , string before , string after ) => string . Concat ( before , value , after ) ;
505
+ public static string Wrap ( this string value , string before , string after ) =>
506
+ string . Concat ( before , value , after ) ;
349
507
}
0 commit comments