-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstrings.go
2715 lines (2618 loc) · 98.5 KB
/
strings.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package unify4g
import (
"crypto/sha256"
"fmt"
"regexp"
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
// IsEmpty checks if the provided string is empty or consists solely of whitespace characters.
//
// The function trims leading and trailing whitespace from the input string `s` using
// strings.TrimSpace. It then evaluates the length of the trimmed string. If the length is
// zero, it indicates that the original string was either empty or contained only whitespace,
// and the function returns true. Otherwise, it returns false.
//
// Parameters:
// - `s`: A string that needs to be checked for emptiness.
//
// Returns:
//
// A boolean value:
// - true if the string is empty or contains only whitespace characters;
// - false if the string contains any non-whitespace characters.
//
// Example:
//
// result := IsEmpty(" ") // result will be true
// result = IsEmpty("Hello") // result will be false
func IsEmpty(s string) bool {
trimmed := strings.TrimSpace(s)
return len(trimmed) == 0
}
// IsAnyEmpty checks if any of the provided strings are empty.
//
// This function takes a variadic number of string arguments and iterates through
// each string to check if it is empty (i.e., has a length of zero or consists
// solely of whitespace characters). If any string in the provided list is found
// to be empty, the function returns `true`. If all strings are non-empty, it
// returns `false`.
//
// Parameters:
// - `strings`: A variadic parameter that allows passing multiple strings to be checked.
//
// Returns:
// - `true` if at least one of the provided strings is empty; `false` if all
// strings are non-empty.
//
// Example:
//
// result := IsAnyEmpty("hello", "", "world") // result will be true because one of the strings is empty.
//
// Notes:
// - The function utilizes the IsEmpty helper function to determine if a string
// is considered empty, which may include strings that are only whitespace.
func IsAnyEmpty(strings ...string) bool {
for _, s := range strings {
if IsEmpty(s) {
return true
}
}
return false
}
// IsNotEmpty checks if the provided string is not empty or does not consist solely of whitespace characters.
//
// This function leverages the IsEmpty function to determine whether the input string `s`
// is empty or contains only whitespace. It returns the negation of the result from IsEmpty.
// If IsEmpty returns true (indicating the string is empty or whitespace), IsNotEmpty will return false,
// and vice versa.
//
// Parameters:
// - `s`: A string that needs to be checked for non-emptiness.
//
// Returns:
//
// A boolean value:
// - true if the string contains at least one non-whitespace character;
// - false if the string is empty or contains only whitespace characters.
//
// Example:
//
// result := IsNotEmpty("Hello") // result will be true
// result = IsNotEmpty(" ") // result will be false
func IsNotEmpty(s string) bool {
return !IsEmpty(s)
}
// IsNoneEmpty checks if all provided strings are non-empty.
//
// This function takes a variadic number of string arguments and iterates through
// each string to verify that none of them are empty (i.e., have a length of zero
// or consist solely of whitespace characters). If any string in the provided list
// is found to be empty, the function immediately returns `false`. If all strings
// are non-empty, it returns `true`.
//
// Parameters:
// - `strings`: A variadic parameter that allows passing multiple strings to be checked.
//
// Returns:
// - `true` if all of the provided strings are non-empty; `false` if at least one
// string is empty.
//
// Example:
//
// result := IsNoneEmpty("hello", "world", "!") // result will be true because all strings are non-empty.
// result2 := IsNoneEmpty("hello", "", "world") // result2 will be false because one of the strings is empty.
//
// Notes:
// - The function utilizes the IsEmpty helper function to determine if a string
// is considered empty, which may include strings that are only whitespace.
func IsNoneEmpty(strings ...string) bool {
for _, s := range strings {
if IsEmpty(s) {
return false
}
}
return true
}
// IsBlank checks if a string is blank (empty or contains only whitespace).
//
// This function determines if the input string `s` is considered blank. A string
// is considered blank if it is either an empty string or consists solely of
// whitespace characters (spaces, tabs, newlines, etc.).
//
// The function first checks if the string is empty. If it is, it returns `true`.
// If the string is not empty, it uses a regular expression to check if the
// string contains only whitespace characters. If the string matches this
// condition, it also returns `true`. If neither condition is met, the function
// returns `false`, indicating that the string contains non-whitespace characters.
//
// Parameters:
// - `s`: The input string to check for blankness.
//
// Returns:
// - `true` if the string is blank (empty or contains only whitespace);
// `false` otherwise.
//
// Example:
//
// result1 := IsBlank("") // result1 will be true because the string is empty.
// result2 := IsBlank(" ") // result2 will be true because the string contains only spaces.
// result3 := IsBlank("Hello") // result3 will be false because the string contains non-whitespace characters.
//
// Notes:
// - The function uses a regular expression to match strings that consist entirely
// of whitespace. The regex `^\s+$` matches strings that contain one or more
// whitespace characters from the start to the end of the string.
func IsBlank(s string) bool {
if s == "" {
return true
}
if regexp.MustCompile(`^\s+$`).MatchString(s) {
return true
}
return false
}
// IsNotBlank checks if a string is not blank (not empty and contains non-whitespace characters).
//
// This function serves as a logical negation of the `IsBlank` function. It checks
// if the input string `s` contains any non-whitespace characters. A string is
// considered not blank if it is neither empty nor consists solely of whitespace
// characters. This is determined by calling the `IsBlank` function and
// negating its result.
//
// Parameters:
// - `s`: The input string to check for non-blankness.
//
// Returns:
// - `true` if the string is not blank (contains at least one non-whitespace character);
// `false` if the string is blank (empty or contains only whitespace).
//
// Example:
//
// result1 := IsNotBlank("Hello") // result1 will be true because the string contains non-whitespace characters.
// result2 := IsNotBlank(" ") // result2 will be false because the string contains only spaces.
// result3 := IsNotBlank("") // result3 will be false because the string is empty.
//
// Notes:
// - This function provides a convenient way to check for meaningful content
// in a string by confirming that it is not blank.
func IsNotBlank(s string) bool {
return !IsBlank(s)
}
// IsAnyBlank checks if any of the provided strings are blank (empty or containing only whitespace).
//
// This function iterates through a variadic list of strings and determines if at least
// one of them is considered blank. A string is considered blank if it is either empty
// or consists solely of whitespace characters. The function returns `true` as soon as
// it finds a blank string; if none of the strings are blank, it returns `false`.
//
// Parameters:
// - `strings`: A variadic parameter that accepts one or more strings to check for blankness.
//
// Returns:
// - `true` if at least one of the provided strings is blank;
// `false` if none of the strings are blank.
//
// Example:
//
// result1 := IsAnyBlank("Hello", "World", " ") // result1 will be true because the third string is blank (contains only a space).
// result2 := IsAnyBlank("Hello", "World") // result2 will be false because both strings are not blank.
//
// Notes:
// - This function is useful for validating input or ensuring that required fields
// are not left blank in forms or data processing.
func IsAnyBlank(strings ...string) bool {
for _, s := range strings {
if IsBlank(s) {
return true
}
}
return false
}
// IsNoneBlank checks if none of the provided strings are blank (empty or containing only whitespace).
//
// This function iterates through a variadic list of strings and returns `false` if any of them
// is blank. A string is considered blank if it is either empty or consists solely of whitespace
// characters. If all strings are non-blank, it returns `true`.
//
// Parameters:
// - `strings`: A variadic parameter that accepts one or more strings to check for blankness.
//
// Returns:
// - `true` if none of the provided strings are blank;
// `false` if at least one of the strings is blank.
//
// Example:
//
// result1 := IsNoneBlank("Hello", "World", " ") // result1 will be false because the third string is blank (contains only a space).
// result2 := IsNoneBlank("Hello", "World") // result2 will be true because both strings are non-blank.
//
// Notes:
// - This function is useful for validating input or ensuring that all required fields
// contain meaningful data in forms or data processing.
func IsNoneBlank(strings ...string) bool {
for _, s := range strings {
if IsBlank(s) {
return false
}
}
return true
}
// IsNumeric checks if the provided string contains only numeric digits (0-9).
//
// This function iterates through each character of the input string and verifies if each
// character is a digit using the `unicode.IsDigit` function. If it encounters any character
// that is not a digit, it returns `false`. If all characters are digits, it returns `true`.
//
// Parameters:
// - `str`: The input string to be checked for numeric characters.
//
// Returns:
// - `true` if the string contains only numeric digits;
// `false` if the string contains any non-numeric characters.
//
// Example:
//
// result1 := IsNumeric("12345") // result1 will be true because the string contains only digits.
// result2 := IsNumeric("123A45") // result2 will be false because the string contains a non-digit character ('A').
//
// Notes:
// - This function is useful for validating numeric input, such as in forms or parsing
// data that should be strictly numeric.
func IsNumeric(str string) bool {
for _, c := range str {
if !unicode.IsDigit(c) {
return false
}
}
return true
}
// IsNumericSpace checks if the provided string contains only numeric digits and whitespace characters.
//
// This function iterates through each character of the input string and verifies if each character
// is either a digit (0-9) or a whitespace character (spaces, tabs, etc.) using the `unicode.IsDigit`
// and `unicode.IsSpace` functions. If it encounters any character that is neither a digit nor a
// whitespace, it returns `false`. If all characters are valid, it returns `true`.
//
// Parameters:
// - `str`: The input string to be checked for numeric digits and whitespace.
//
// Returns:
// - `true` if the string contains only digits and whitespace;
// `false` if the string contains any non-numeric and non-whitespace characters.
//
// Example:
//
// result1 := IsNumericSpace("123 456") // result1 will be true because the string contains only digits and a space.
// result2 := IsNumericSpace("123A456") // result2 will be false because the string contains a non-numeric character ('A').
//
// Notes:
// - This function is useful for validating input that should be a number but may also include
// spaces, such as in user forms or data processing where formatting is flexible.
func IsNumericSpace(str string) bool {
for _, c := range str {
if !unicode.IsDigit(c) && !unicode.IsSpace(c) {
return false
}
}
return true
}
// IsWhitespace checks if the provided string contains only whitespace characters.
//
// This function iterates through each character of the input string and checks if each character
// is a whitespace character (spaces, tabs, newlines, etc.) using the `unicode.IsSpace` function.
// If it encounters any character that is not a whitespace, it returns `false`. If all characters
// are whitespace, it returns `true`.
//
// Parameters:
// - `str`: The input string to be checked for whitespace.
//
// Returns:
// - `true` if the string contains only whitespace characters;
// `false` if the string contains any non-whitespace characters.
//
// Example:
//
// result1 := IsWhitespace(" ") // result1 will be true because the string contains only spaces.
// result2 := IsWhitespace("Hello") // result2 will be false because the string contains non-whitespace characters.
//
// Notes:
// - This function is useful for determining if a string is blank in terms of visible content,
// which can be important in user input validation or string processing tasks.
func IsWhitespace(str string) bool {
for _, c := range str {
if !unicode.IsSpace(c) {
return false
}
}
return true
}
// TrimWhitespace removes extra whitespace from the input string,
// replacing any sequence of whitespace characters with a single space.
//
// This function first checks if the input string `s` is empty or consists solely of whitespace
// using the IsEmpty function. If so, it returns an empty string. If the string contains
// non-whitespace characters, it utilizes a precompiled regular expression (regexpDupSpaces)
// to identify and replace all sequences of whitespace characters (including spaces, tabs, and
// newlines) with a single space. This helps to normalize whitespace in the string.
//
// Parameters:
// - `s`: The input string from which duplicate whitespace needs to be removed.
//
// Returns:
// - A string with all sequences of whitespace characters replaced by a single space.
// If the input string is empty or only contains whitespace, an empty string is returned.
//
// Example:
//
// result := TrimWhitespace("This is an example.\n\nThis is another line.") // result will be "This is an example. This is another line."
func TrimWhitespace(s string) string {
if IsEmpty(s) {
return ""
}
// Use a regular expression to replace all sequences of whitespace characters with a single space.
s = RegexpDupSpaces.ReplaceAllString(s, " ")
return s
}
// CleanSpaces removes leading and trailing whitespace characters from a given string and replaces sequences of whitespace characters with a single space.
// It first checks if the input string is empty or consists solely of whitespace characters. If so, it returns an empty string.
// Otherwise, it calls TrimWhitespace to replace all sequences of whitespace characters with a single space, effectively removing duplicates.
// Finally, it trims the leading and trailing whitespace characters from the resulting string using strings.TrimSpace and returns the cleaned string.
func CleanSpaces(s string) string {
if IsEmpty(s) {
return ""
}
return strings.TrimSpace(TrimWhitespace(s))
}
// Trim removes leading and trailing whitespace characters from a string.
// The function iteratively checks and removes spaces (or any character less than or equal to a space)
// from both the left (beginning) and right (end) of the string.
//
// Parameters:
// - s: A string that may contain leading and trailing whitespace characters that need to be removed.
//
// Returns:
// - A new string with leading and trailing whitespace removed. The function does not modify the original string,
// as strings in Go are immutable.
//
// Example Usage:
//
// str := " hello world "
// trimmed := Trim(str)
// // trimmed: "hello world" (leading and trailing spaces removed)
//
// str = "\n\n Trim me \t\n"
// trimmed = Trim(str)
// // trimmed: "Trim me" (leading and trailing spaces and newline characters removed)
//
// Details:
//
// - The function works by iteratively removing any characters less than or equal to a space (ASCII 32) from the
// left side of the string until no such characters remain. It then performs the same operation on the right side of
// the string until no whitespace characters are left.
//
// - The function uses a `goto` mechanism to handle the removal in a loop, which ensures all leading and trailing
// spaces (or any whitespace characters) are removed without additional checks for length or condition evaluation
// in every iteration.
//
// - The trimmed result string will not contain leading or trailing whitespace characters after the function completes.
//
// - The function returns an unchanged string if no whitespace is present.
func Trim(s string) string {
if IsEmpty(s) {
return s
}
left:
if len(s) > 0 && s[0] <= ' ' {
s = s[1:]
goto left
}
right:
if len(s) > 0 && s[len(s)-1] <= ' ' {
s = s[:len(s)-1]
goto right
}
return s
}
// Quote formats a string argument for safe output, escaping any special characters
// and enclosing the result in double quotes.
//
// This function uses the fmt.Sprintf function with the %#q format verb to create a quoted
// string representation of the input argument `arg`. The output will escape any special
// characters (such as newlines or tabs) in the string, ensuring that it is suitable for
// safe display or logging. The resulting string will be surrounded by double quotes,
// making it clear where the string begins and ends.
//
// Parameters:
// - `arg`: The input string to be formatted.
//
// Returns:
// - A string that represents the input `arg` as a quoted string with special characters
// escaped. This can be useful for creating safe outputs in logs or console displays.
//
// Example:
//
// formatted := Quote("Hello, world!\nNew line here.") // formatted will be "\"Hello, world!\\nNew line here.\""
func Quote(arg string) string {
return fmt.Sprintf("%#q", arg)
}
// TrimPrefixAll returns a new string with all occurrences of prefix at the start of s removed.
// If prefix is the empty string, this function returns s.
func TrimPrefixAll(s string, prefix string) string {
if IsEmpty(prefix) {
return s
}
for strings.HasPrefix(s, prefix) {
s = s[len(prefix):]
}
return s
}
// TrimPrefixN returns a new string with up to n occurrences of prefix at the start of s removed.
// If prefix is the empty string, this function returns s.
// If n is negative, returns TrimPrefixAll(s, prefix).
func TrimPrefixN(s string, prefix string, n int) string {
if n < 0 {
return TrimPrefixAll(s, prefix)
}
if IsEmpty(prefix) {
return s
}
for n > 0 && strings.HasPrefix(s, prefix) {
s = s[len(prefix):]
n--
}
return s
}
// TrimSuffixAll returns a new string with all occurrences of suffix at the end of s removed.
// If suffix is the empty string, this function returns s.
func TrimSuffixAll(s string, suffix string) string {
if IsEmpty(suffix) {
return s
}
for strings.HasSuffix(s, suffix) {
s = s[:len(s)-len(suffix)]
}
return s
}
// TrimSuffixN returns a new string with up to n occurrences of suffix at the end of s removed.
// If suffix is the empty string, this function returns s.
// If n is negative, returns TrimSuffixAll(s, suffix).
func TrimSuffixN(s string, suffix string, n int) string {
if n < 0 {
return TrimSuffixAll(s, suffix)
}
if IsEmpty(suffix) {
return s
}
for n > 0 && strings.HasSuffix(s, suffix) {
s = s[:len(s)-len(suffix)]
n--
}
return s
}
// TrimSequenceAll returns a new string with all occurrences of sequence at the start and end of s removed.
// If sequence is the empty string, this function returns s.
func TrimSequenceAll(s string, sequence string) string {
return TrimSuffixAll(TrimPrefixAll(s, sequence), sequence)
}
// ReplaceAllStrings takes a slice of strings and replaces all occurrences of a specified
// substring (old) with a new substring (new) in each string of the slice.
//
// This function creates a new slice of strings, where each string is the result of
// replacing all instances of the old substring with the new substring in the corresponding
// string from the input slice. The original slice remains unchanged.
//
// Parameters:
// - `ss`: A slice of strings in which the replacements will be made.
// - `old`: The substring to be replaced.
// - `new`: The substring to replace the old substring with.
//
// Returns:
// - A new slice of strings with all occurrences of `old` replaced by `new` in each string
// from the input slice.
//
// Example:
//
// input := []string{"hello world", "world peace", "goodbye world"}
// output := ReplaceAllStrings(input, "world", "universe") // output will be []string{"hello universe", "universe peace", "goodbye universe"}
func ReplaceAllStrings(ss []string, old string, new string) []string {
values := make([]string, len(ss))
for i, s := range ss {
values[i] = strings.ReplaceAll(s, old, new)
}
return values
}
// Slash is like strings.Join(elems, "/"), except that all leading and trailing occurrences of '/'
// between elems are trimmed before they are joined together. Non-trailing leading slashes in the
// first element as well as non-leading trailing slashes in the last element are kept.
func Slash(elems ...string) string {
return JoinUnary(elems, "/")
}
// JoinUnary concatenates a slice of strings into a single string, separating each element
// with a specified separator. The function handles various cases of input size and optimizes
// memory allocation based on expected lengths.
//
// Parameters:
// - `elems`: A slice of strings to be concatenated.
// - `separator`: A string used to separate the elements in the final concatenated string.
//
// Returns:
// - A single string resulting from the concatenation of the input strings, with the specified
// separator inserted between each element. If the slice is empty, it returns an empty string.
// If there is only one element in the slice, it returns that element without any separators.
//
// The function performs the following steps:
// 1. Checks if the input slice is empty; if so, it returns an empty string.
// 2. If the slice contains a single element, it returns that element directly.
// 3. A `strings.Builder` is used to efficiently build the output string, with an initial capacity
// that is calculated based on the number of elements and their average length.
// 4. Each element is appended to the builder, with the specified separator added between them.
// 5. The function also trims any leading or trailing occurrences of the separator from each element
// to avoid duplicate separators in the output.
//
// Example:
//
// elems := []string{"apple", "banana", "cherry"}
// separator := ", "
// result := JoinUnary(elems, separator) // result will be "apple, banana, cherry"
func JoinUnary(elems []string, separator string) string {
if len(elems) == 0 {
return ""
}
if len(elems) == 1 {
return elems[0]
}
var sb strings.Builder
const maxGuess = 100
const guessAverageElementLen = 5
if len(elems) <= maxGuess {
sb.Grow((len(elems)-1)*len(separator) + len(elems)*guessAverageElementLen)
} else {
sb.Grow((len(elems)-1)*len(separator) + maxGuess*guessAverageElementLen)
}
t := TrimSuffixAll(elems[0], separator) + separator
for _, element := range elems[1 : len(elems)-1] {
sb.WriteString(t)
t = TrimSequenceAll(element, separator) + separator
}
sb.WriteString(t)
t = TrimPrefixAll(elems[len(elems)-1], separator)
sb.WriteString(t)
return sb.String()
}
// Reverse returns a new string that is the reverse of the input string s.
// This function handles multi-byte Unicode characters correctly by operating on runes,
// ensuring that each character is reversed without corrupting the character encoding.
//
// Parameters:
// - `s`: A string to be reversed.
//
// Returns:
// - A new string that contains the characters of the input string in reverse order.
// If the input string has fewer than two characters (i.e., is empty or a single character),
// it returns the input string as-is.
//
// The function works as follows:
// 1. It checks the length of the input string using utf8.RuneCountInString. If the string has
// fewer than two characters, it returns the original string.
// 2. The input string is converted to a slice of runes to correctly handle multi-byte characters.
// 3. A buffer of runes is created to hold the reversed characters.
// 4. A loop iterates over the original rune slice from the end to the beginning, copying each
// character into the buffer in reverse order.
// 5. Finally, the function converts the buffer back to a string and returns it.
//
// Example:
//
// original := "hello"
// reversed := Reverse(original) // reversed will be "olleh"
func Reverse(s string) string {
if utf8.RuneCountInString(s) < 2 {
return s
}
r := []rune(s)
buffer := make([]rune, len(r))
for i, j := len(r)-1, 0; i >= 0; i-- {
buffer[j] = r[i]
j++
}
return string(buffer)
}
// Hash computes the SHA256 hash of the input string s and returns it as a hexadecimal string.
//
// This function performs the following steps:
// 1. It checks if the input string is empty or consists only of whitespace characters using the IsEmpty function.
// If the string is empty, it returns the original string.
// 2. It creates a new SHA256 hash using the sha256.New() function.
// 3. The input string is converted to a byte slice and written to the hash. If an error occurs during this process,
// the function returns an empty string.
// 4. Once the string has been written to the hash, it calculates the final hash value using the Sum method.
// 5. The hash value is then formatted as a hexadecimal string using fmt.Sprintf and returned.
//
// Parameters:
// - `s`: The input string to be hashed.
//
// Returns:
// - A string representing the SHA256 hash of the input string in hexadecimal format.
// If the input string is empty or if an error occurs during hashing, an empty string is returned.
//
// Example:
//
// input := "hello"
// hashValue := Hash(input) // hashValue will contain the SHA256 hash of "hello" in hexadecimal format.
//
// Notes:
// - This function is suitable for generating hash values for strings that can be used for comparisons,
// checksums, or other cryptographic purposes. However, if the input string is empty, it returns the empty
// string as a direct response.
func Hash(s string) string {
// Check if the input string is empty or consists solely of whitespace characters
if IsEmpty(s) {
return s
}
// Create a new SHA256 hash
h := sha256.New()
// Write the input string to the hash
if _, err := h.Write([]byte(s)); err != nil {
// If an error occurs during the write process, return an empty string
return ""
}
// Sum the hash to get the final hash value
sum := h.Sum(nil)
// Format the hash value as a hexadecimal string and return it
return fmt.Sprintf("%x", sum)
}
// OnlyLetters returns a new string containing only the letters from the original string, excluding all non-letter characters such as numbers, spaces, and special characters.
// This function iterates through each character in the input string, checks if it is a letter using the unicode.IsLetter function, and appends it to a slice of runes if it is.
// The function returns a string created from the slice of letters.
func OnlyLetters(sequence string) string {
// Check if the input string is empty or consists solely of whitespace characters
if IsEmpty(sequence) {
return ""
}
// Check if the input string has no runes (e.g., it contains only whitespace characters)
if utf8.RuneCountInString(sequence) == 0 {
return ""
}
// Initialize a slice to store the letters found in the input string
var letters []rune
// Iterate through each character in the input string
for _, r := range sequence {
// Check if the current character is a letter
if unicode.IsLetter(r) {
// If it is a letter, append it to the slice of letters
letters = append(letters, r)
}
}
// Convert the slice of letters back into a string and return it
return string(letters)
}
// OnlyDigits returns a new string containing only the digits from the original string, excluding all non-digit characters such as letters, spaces, and special characters.
// This function first checks if the input string is empty or consists solely of whitespace characters. If so, it returns an empty string.
// If the input string is not empty, it uses a regular expression to replace all non-digit characters with an empty string, effectively removing them.
// The function returns the resulting string, which contains only the digits from the original string.
func OnlyDigits(sequence string) string {
if IsEmpty(sequence) {
return ""
}
if utf8.RuneCountInString(sequence) > 0 {
re, _ := regexp.Compile(`[\D]`)
sequence = re.ReplaceAllString(sequence, "")
}
return sequence
}
// Indent takes a string `s` and a string `left`, and indents every line in `s` by prefixing it with `left`.
// Empty lines are also indented.
//
// Parameters:
// - `s`: The input string whose lines will be indented. It may contain multiple lines separated by newline characters (`\n`).
// - `left`: The string that will be used as the indentation prefix. This string is prepended to every line of `s`, including empty lines.
//
// Behavior:
// - The function works by replacing each newline character (`\n`) in `s` with a newline followed by the indentation string `left`.
// - It also adds `left` to the beginning of the string, ensuring the first line is indented.
// - Empty lines, if present, are preserved and indented like non-empty lines.
//
// Returns:
// - A new string where every line of the input `s` has been indented by the string `left`.
//
// Example:
//
// Input:
//
// s = "Hello\nWorld\n\nThis is a test"
// left = ">>> "
//
// Output:
//
// ">>> Hello\n>>> World\n>>> \n>>> This is a test"
//
// In this example, each line of the input, including the empty line, is prefixed with ">>> ".
func Indent(s string, left string) string {
return left + strings.Replace(s, "\n", "\n"+left, -1)
}
// RemoveAccents removes accents and diacritics from the input string s,
// converting special characters into their basic ASCII equivalents.
//
// This function processes each rune in the input string and uses the
// normalizeRune function to convert accented characters to their unaccented
// counterparts. The results are collected in a strings.Builder for efficient
// string concatenation.
//
// Parameters:
// - `s`: The input string from which accents and diacritics are to be removed.
//
// Returns:
// - A new string that contains the same characters as the input string,
// but with all accents and diacritics removed. Characters that do not
// have a corresponding unaccented equivalent are returned as they are.
//
// Example:
//
// input := "Café naïve"
// output := RemoveAccents(input) // output will be "Cafe naive"
//
// Notes:
// - This function is useful for normalizing strings for comparison,
// searching, or displaying in a consistent format. It relies on the
// normalizeRune function to perform the actual character conversion.
func RemoveAccents(s string) string {
var buff strings.Builder
buff.Grow(len(s))
for _, r := range s {
buff.WriteString(normalize_rune(r))
}
return buff.String()
}
// Slugify converts a string to a slug which is useful in URLs, filenames.
// It removes accents, converts to lower case, remove the characters which
// are not letters or numbers and replaces spaces with "-".
//
// Example:
//
// unify4g.Slugify("'We löve Motörhead'") //Output: we-love-motorhead
//
// Normalzation is done with unify4g.ReplaceAccents function using a rune replacement map
// You can use the following code for better normalization before unify4g.Slugify()
//
// str := "'We löve Motörhead'"
// t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
// str = transform.String(t, str) //We love Motorhead
//
// Slugify doesn't support transliteration. You should use a transliteration
// library before Slugify like github.com/rainycape/unidecode
//
// Example:
//
// import "github.com/rainycape/unidecode"
//
// str := unidecode.Unidecode("你好, world!")
// unify4g.Slugify(str) //Output: ni-hao-world
func Slugify(s string) string {
return SlugifySpecial(s, "-")
}
// SlugifySpecial converts a string to a slug with the delimiter.
// It removes accents, converts string to lower case, remove the characters
// which are not letters or numbers and replaces spaces with the delimiter.
//
// Example:
//
// unify4g.SlugifySpecial("'We löve Motörhead'", "-") //Output: we-love-motorhead
//
// SlugifySpecial doesn't support transliteration. You should use a transliteration
// library before SlugifySpecial like github.com/rainycape/unidecode
//
// Example:
//
// import "github.com/rainycape/unidecode"
//
// str := unidecode.Unidecode("你好, world!")
// unify4g.SlugifySpecial(str, "-") //Output: ni-hao-world
func SlugifySpecial(str string, delimiter string) string {
str = RemoveAccents(str)
delBytes := []byte(delimiter)
n := make([]byte, 0, len(str))
isPrevSpace := false
for _, r := range str {
if r >= 'A' && r <= 'Z' {
r -= 'A' - 'a'
}
//replace non-alpha chars with delimiter
switch {
case (r >= 'a' && r <= 'z') || (r >= '0' && r <= '9'):
n = append(n, byte(int8(r)))
isPrevSpace = false
case !isPrevSpace:
if len(n) > 0 {
n = append(n, delBytes...)
}
fallthrough
default:
isPrevSpace = true
}
}
ln := len(n)
ld := len(delimiter)
if ln >= ld && string(n[ln-ld:]) == delimiter {
n = n[:ln-ld]
}
return string(n)
}
// ToSnakeCase converts the input string s to snake_case format,
// where all characters are lowercase and spaces are replaced with underscores.
//
// This function first trims any leading or trailing whitespace from the input
// string and then converts all characters to lowercase. It subsequently
// replaces all spaces in the string with underscores to achieve the desired
// snake_case format.
//
// Parameters:
// - `s`: The input string to be converted to snake_case.
//
// Returns:
// - A new string formatted in snake_case. If the input string is empty or
// contains only whitespace, the function will return an empty string.
//
// Example:
//
// input := "Hello World"
// output := ToSnakeCase(input) // output will be "hello_world"
//
// Notes:
// - This function is useful for generating variable names, file names,
// or other identifiers that conform to snake_case naming conventions.
func ToSnakeCase(s string) string {
s = strings.TrimSpace(strings.ToLower(s))
return strings.Replace(s, " ", "_", -1)
}
// ToCamelCase converts the input string s to CamelCase format,
// where the first letter of each word is capitalized and all spaces
// are removed.
//
// This function first trims any leading or trailing whitespace from the input
// string. It then iterates over each character in the string, capitalizing the
// first character of each word (defined as a sequence of characters following
// a space) while removing all spaces from the final result. The first character
// of the string remains unchanged unless it follows a space.
//
// Parameters:
// - `s`: The input string to be converted to CamelCase.
//
// Returns:
// - A new string formatted in CamelCase. If the input string has fewer than
// two characters, it returns the original string unchanged. If the input
// string contains only spaces, it returns an empty string.
//
// Example:
//
// input := "hello world"
// output := ToCamelCase(input) // output will be "HelloWorld"
//
// Notes:
// - This function is useful for generating variable names or identifiers that
// conform to CamelCase naming conventions.
func ToCamelCase(s string) string {
s = strings.TrimSpace(s)
if Len(s) < 2 {
return s
}
var buff strings.Builder
var prev string
for _, r := range s {
c := string(r)
if c != " " {
if prev == " " {
c = strings.ToUpper(c)
}
buff.WriteString(c)
}
prev = c
}
return buff.String()
}
// SplitCamelCase splits a CamelCase string into its component words.
//
// This function takes a string in CamelCase format and separates it into
// individual words based on transitions between upper and lower case letters,
// as well as transitions between letters and digits. It handles the following cases:
//
// - A transition from a lowercase letter to an uppercase letter indicates the
// start of a new word.
// - A transition from an uppercase letter to a lowercase letter indicates
// the continuation of a word, unless preceded by a digit.
// - A digit following a letter also indicates a split between words.
//
// The function also trims any leading or trailing whitespace from the input string.
// If the input string has fewer than two characters, it returns a slice containing
// the original string.
//
// Parameters:
// - `s`: The input CamelCase string to be split into words.
//
// Returns:
// - A slice of strings containing the individual words extracted from the
// input string.
//
// Example:
//
// input := "CamelCaseString123"
// output := SplitCamelCase(input) // output will be []string{"Camel", "Case", "String", "123"}
//
// Notes:
// - This function is useful for parsing identifiers or names that follow
// CamelCase conventions, making them easier to read and understand.
func SplitCamelCase(s string) []string {
s = strings.TrimSpace(s)
if Len(s) < 2 {
return []string{s}
}
var prev rune
var start int
words := []string{}
runes := []rune(s)
for i, r := range runes {
if i != 0 {
switch {
case unicode.IsDigit(r) && unicode.IsLetter(prev):
fallthrough
case unicode.IsUpper(r) && unicode.IsLower(prev):
words = append(words, string(runes[start:i]))
start = i
case unicode.IsLower(r) && unicode.IsUpper(prev) && start != i-1:
words = append(words, string(runes[start:i-1]))
start = i - 1
}