|
1 | | -# stringr 🧵 |
| 1 | +# stringr |
2 | 2 |
|
3 | | -String manipulation in dart/flutter on steroids! ⚡⚡ |
4 | | -This dart plugin is null safety compliant 😎😎 |
5 | | - |
6 | | - |
7 | | - |
| 3 | +[](https://pub.dev/packages/stringr) |
| 4 | +[](https://codecov.io/gh/Chinmay-KB/stringr) |
8 | 5 | [](https://opensource.org/licenses/MIT) |
9 | 6 |
|
| 7 | +A comprehensive string manipulation library for Dart and Flutter. Inspired by [VocaJS](https://vocajs.com/), stringr provides powerful string operations that work seamlessly with Latin, non-Latin, and Unicode strings. |
10 | 8 |
|
11 | | -This dart plugin is inspired 🧠 by [Voca js](https://vocajs.com/#) library. This plugin includes all the nifty functions required for powerful string manipulations. |
| 9 | +## Features |
12 | 10 |
|
13 | | -How powerful? Glad you asked!! |
| 11 | +- **Case Conversion** - camelCase, snake_case, kebab-case, Title Case, PascalCase |
| 12 | +- **Unicode Support** - Full support for non-Latin scripts, diacritics, and grapheme clusters |
| 13 | +- **Smart Word Detection** - Uses regex patterns instead of simple whitespace splitting |
| 14 | +- **Null Safety** - Fully null-safe and compatible with Dart 3.0+ |
| 15 | +- **Zero Dependencies** - Only depends on the `characters` package for grapheme support |
14 | 16 |
|
15 | | -This plugin works on both latin and non latin strings, and uses complex regular expressions to extract words from strings on manipulations, rather than just calling `split(" ")` on a string. |
16 | | -```dart |
| 17 | +## Installation |
17 | 18 |
|
18 | | -import 'package:stringr/stringr.dart'; |
| 19 | +Add to your `pubspec.yaml`: |
19 | 20 |
|
20 | | -void main(){ |
21 | | - print("camel case".camelCase()); // Output - "camelCase" |
22 | | - print("HTMLview".camelCase(preserveAcronym=true)); // Output - "HTMLView" |
23 | | - print("/this/is/a/directory".camelCase()); // Output - "thisIsADirectory" |
24 | | - print("полет птицы".kebabCase()); // Output - "полет_птицы" |
25 | | - print("La variété la plus fréquente est la blanche".prune(12)); // Output - La variété |
26 | | -print("2infinity*@#@AND93beyond".words()); // Output - ["2", "infinity", "AND", "93", "beyond"] |
27 | | - print("gravity".words(pattern: r"\w{1,2}")); // Output - ['gr', 'av', 'it', 'y'] |
28 | | -} |
29 | | - |
| 21 | +```yaml |
| 22 | +dependencies: |
| 23 | + stringr: ^1.2.0 |
30 | 24 | ``` |
31 | | -Convert a string to its latin counterpart! Can reverse not only pure strings, but strings with grapheme clusters as well😎 |
32 | 25 |
|
33 | | -```dart |
34 | | -
|
35 | | -import 'package:stringr/stringr.dart'; |
36 | | -
|
37 | | -void main(){ |
38 | | -print("cafe\u0301".latinize()); // Output - cafe |
39 | | -print("anglikonų šiurkščios užrašinėti".latinize());// Output - 'anglikonu siurkscios uzrasineti' |
40 | | -print("𝌆 bar mañana mañana".reverse()); // Output - 'anañam anañam rab 𝌆' |
41 | | -// Splitting and reversing normally would give this as output |
42 | | -// anãnam anañam rab �� |
43 | | -} |
44 | | - |
45 | | -``` |
46 | | -Support for graphemes is also included in this plugin. [characters](https://pub.dev/packages/characters) plugin is used to facilitate some of the functionalities. |
| 26 | +## Quick Start |
47 | 27 |
|
48 | 28 | ```dart |
49 | | -
|
50 | 29 | import 'package:stringr/stringr.dart'; |
51 | 30 |
|
52 | | -void main(){ |
53 | | - print("cafe\u0301".countGrapheme()); // Output - 4 |
54 | | - print("\uD835\uDC00\uD835\uDC01".countGrapheme()); // Output - 2 |
55 | | - print("\uD835\uDC00\uD835\uDC01".graphemeAt(1)); // Output - "\uD835\uDC01" |
56 | | -print("stellar bomb".codePoints()); // Output - [0x73, 0x74, 0x65, 0x6c, 0x6c, 0x61, 0x72, 0x20, 0x62, 0x6f, 0x6d, 0x62] |
57 | | - print('man\u0303ana'.graphemes()); // Output - ['m','a', 'n\u0303', 'a', 'n', 'a'] |
| 31 | +void main() { |
| 32 | + // Case conversions |
| 33 | + print('hello world'.toCamelCase); // helloWorld |
| 34 | + print('hello world'.toSnakeCase); // hello_world |
| 35 | + print('hello world'.toKebabCase); // hello-world |
| 36 | + print('hello world'.toPascalCase); // HelloWorld |
| 37 | + |
| 38 | + // Smart word extraction |
| 39 | + print('XMLHttpRequest'.camelCase()); // xmlHttpRequest |
| 40 | + print('/path/to/file'.camelCase()); // pathToFile |
| 41 | + print('2infinity&beyond'.words()); // [2, infinity, beyond] |
58 | 42 | } |
59 | | - |
60 | 43 | ``` |
61 | | -HTML strings can be escaped! |
62 | 44 |
|
63 | | -```dart |
| 45 | +## Unicode & Non-Latin Support |
64 | 46 |
|
65 | | -import 'package:stringr/stringr.dart'; |
| 47 | +stringr shines with complex Unicode strings: |
66 | 48 |
|
67 | | -void main(){ |
68 | | -print('<>&"\'`'.escapeHTML()); // Output - '<>&"'`' |
69 | | -print('<p>wonderful world</p>'.escapeHTML());// Output - '<p>wonderful world</p>' |
70 | | -} |
71 | | - |
72 | | -``` |
73 | | -You can count on stringr to count the words, latin, non latin, or strings with grapheme clusters! |
74 | 49 | ```dart |
| 50 | +// Non-Latin scripts |
| 51 | +print('полет птицы'.kebabCase()); // полет-птицы |
| 52 | +print('La variété française'.prune(12)); // La variété |
75 | 53 |
|
76 | | -import 'package:stringr/stringr.dart'; |
| 54 | +// Diacritics and latinization |
| 55 | +print('café'.latinize()); // cafe |
| 56 | +print('anglikonų šiurkščios'.latinize()); // anglikonu siurkscios |
77 | 57 |
|
78 | | -void main(){ |
79 | | -print("a year without rain".count()); // Output - 19 |
80 | | -print("foo\uD834\uDF06\u0303\u035C\u035D\u035Ebar".countGrapheme());// Output - 7 |
81 | | -print("to be or not to be".countOccurences("to")); // Output - 2 |
82 | | -print("aBCdeFgH".countWhere((character) => character.isAlphabet())); // Output - 8 |
83 | | -print("NewYork".countWords()); // Output - 2 |
84 | | -} |
85 | | - |
| 58 | +// Grapheme-aware operations |
| 59 | +print('café'.countGrapheme()); // 4 (not 5!) |
| 60 | +print('👨👩👧👦'.reverse()); // 👨👩👧👦 (family emoji stays intact) |
86 | 61 | ``` |
87 | | -* Some of the functionalities, like `char()` and `codePoints` are either simple enough to implement or were available in the characters package. But still I decided to add them to make bundle everything under a similar syntax ( i.e- calling the extension functions on string). |
88 | | -* For functions like `padLeft()` and `trim()` which already are extension functions and inbuilt in dart, I have not included them in this plugin as the syntax would be similar to that of this plugin. |
| 62 | + |
| 63 | +## API Overview |
| 64 | + |
| 65 | +### Case Manipulation |
| 66 | +| Method | Description | Example | |
| 67 | +|--------|-------------|---------| |
| 68 | +| `camelCase()` | Convert to camelCase | `'foo bar'` → `'fooBar'` | |
| 69 | +| `snakeCase()` | Convert to snake_case | `'foo bar'` → `'foo_bar'` | |
| 70 | +| `kebabCase()` | Convert to kebab-case | `'foo bar'` → `'foo-bar'` | |
| 71 | +| `titleCase()` | Capitalize each word | `'foo bar'` → `'Foo Bar'` | |
| 72 | +| `capitalize()` | Capitalize first char | `'foo'` → `'Foo'` | |
| 73 | +| `swapCase()` | Toggle case | `'FoO'` → `'fOo'` | |
| 74 | + |
| 75 | +### String Chopping |
| 76 | +| Method | Description | Example | |
| 77 | +|--------|-------------|---------| |
| 78 | +| `truncate(n)` | Truncate to n chars | `'hello world'` → `'hello...'` | |
| 79 | +| `prune(n)` | Truncate without breaking words | `'hello world'` → `'hello'` | |
| 80 | +| `first(n)` | Get first n chars | `'hello'` → `'hel'` | |
| 81 | +| `last(n)` | Get last n chars | `'hello'` → `'llo'` | |
| 82 | +| `slice(start, end)` | Extract substring | `'hello'` → `'ell'` | |
| 83 | + |
| 84 | +### Counting |
| 85 | +| Method | Description | Example | |
| 86 | +|--------|-------------|---------| |
| 87 | +| `countGrapheme()` | Count visible characters | `'café'` → `4` | |
| 88 | +| `countOccurrences(s)` | Count substring occurrences | `'banana'.countOccurrences('a')` → `3` | |
| 89 | +| `countWords()` | Count words | `'hello world'` → `2` | |
| 90 | +| `countWhere(fn)` | Count matching chars | Custom predicate | |
| 91 | + |
| 92 | +### Manipulation |
| 93 | +| Method | Description | Example | |
| 94 | +|--------|-------------|---------| |
| 95 | +| `reverse()` | Reverse (grapheme-aware) | `'hello'` → `'olleh'` | |
| 96 | +| `slugify()` | URL-friendly slug | `'Hello World!'` → `'hello-world'` | |
| 97 | +| `latinize()` | Remove diacritics | `'café'` → `'cafe'` | |
| 98 | +| `insert(s, i)` | Insert at position | `'hllo'.insert('e', 1)` → `'hello'` | |
| 99 | + |
| 100 | +### Escaping |
| 101 | +| Method | Description | |
| 102 | +|--------|-------------| |
| 103 | +| `escapeHtml()` | Escape HTML entities | |
| 104 | +| `unescapeHtml()` | Unescape HTML entities | |
| 105 | +| `escapeRegExp()` | Escape regex special chars | |
| 106 | + |
| 107 | +## Why stringr? |
| 108 | + |
| 109 | +Most string libraries fail with complex Unicode. stringr handles: |
| 110 | + |
| 111 | +- **Grapheme clusters**: `'café'` is 4 characters, not 5 |
| 112 | +- **Surrogate pairs**: Emoji and special symbols work correctly |
| 113 | +- **Non-Latin scripts**: Cyrillic, Greek, accented Latin, and more |
| 114 | +- **Smart word detection**: `'XMLHttpRequest'` → `['XML', 'Http', 'Request']` |
| 115 | + |
| 116 | +## Contributing |
| 117 | + |
| 118 | +Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details. |
| 119 | + |
| 120 | +## License |
| 121 | + |
| 122 | +MIT License - see [LICENSE](LICENSE) for details. |
0 commit comments