OLD | NEW |
(Empty) | |
| 1 = Dart Cookbook |
| 2 :author: Shailen Tuli |
| 3 :encoding: UTF-8 |
| 4 |
| 5 == Strings |
| 6 |
| 7 === Concatenating Strings |
| 8 |
| 9 ==== Problem |
| 10 |
| 11 You want to concatenate strings in Dart. You tried using `+`, but that |
| 12 resulted in an error. |
| 13 |
| 14 ==== Solution |
| 15 |
| 16 Use adjacent string literals: |
| 17 |
| 18 -------------------------------------------------- |
| 19 var fact = 'Dart' 'is' ' fun!'; // 'Dart is fun!' |
| 20 -------------------------------------------------- |
| 21 |
| 22 ==== Discussion |
| 23 |
| 24 Adjacent literals work over multiple lines: |
| 25 |
| 26 ------------------------- |
| 27 var fact = 'Dart' |
| 28 'is' |
| 29 'fun!'; // 'Dart is fun!' |
| 30 ------------------------- |
| 31 |
| 32 They also work when using multiline strings: |
| 33 |
| 34 ---------------------------------------- |
| 35 var lunch = '''Peanut |
| 36 butter''' |
| 37 '''and |
| 38 jelly'''; // 'Peanut\nbutter and\njelly' |
| 39 ---------------------------------------- |
| 40 |
| 41 You can concatenate adjacent single line literals with multiline |
| 42 strings: |
| 43 |
| 44 -------------------------------------- |
| 45 var funnyGuys = 'Dewey ' 'Cheatem' |
| 46 ''' and |
| 47 Howe'''; // 'Dewey Cheatem and\n Howe' |
| 48 -------------------------------------- |
| 49 |
| 50 ===== Alternatives to adjacent string literals |
| 51 |
| 52 You can use the `concat()` method on a string to concatenate it to |
| 53 another string: |
| 54 |
| 55 --------------------------------------------------- |
| 56 var film = filmToWatch(); |
| 57 film = film.concat('\n'); // 'The Big Lebowski\n' |
| 58 --------------------------------------------------- |
| 59 |
| 60 Because `concat()` creates a new string every time it is invoked, a long |
| 61 chain of `concat()` s can be expensive. Avoid those. Use a StringBuffer |
| 62 instead (see _Incrementally building a string efficiently using a |
| 63 StringBuffer_, below). |
| 64 |
| 65 Use `join()` to combine a sequence of strings: |
| 66 |
| 67 ----------------------------------------------------------------------- |
| 68 var film = ['The', 'Big', 'Lebowski']).join(' '); // 'The Big Lebowski' |
| 69 ----------------------------------------------------------------------- |
| 70 |
| 71 You can also use string interpolation to concatenate strings (see |
| 72 _Interpolating expressions inside strings_, below). |
| 73 |
| 74 === Interpolating expressions inside strings |
| 75 |
| 76 ==== Problem |
| 77 |
| 78 You want to embed Dart code inside strings. |
| 79 |
| 80 ==== Solution |
| 81 |
| 82 You can put the value of an expression inside a string by using |
| 83 $\{expression}. |
| 84 |
| 85 ---------------------------------------------------------------------- |
| 86 var favFood = 'sushi'; |
| 87 var whatDoILove = 'I love ${favFood.toUpperCase()}'; // 'I love SUSHI' |
| 88 ---------------------------------------------------------------------- |
| 89 |
| 90 You can skip the \{} if the expression is an identifier: |
| 91 |
| 92 ------------------------------------------------------ |
| 93 var whatDoILove = 'I love $favFood'; // 'I love sushi' |
| 94 ------------------------------------------------------ |
| 95 |
| 96 ==== Discussion |
| 97 |
| 98 An interpolated string, `'string ${expression}` is equivalent to the |
| 99 concatenation of the strings `string` and `expression.toString()`. |
| 100 Consider this code: |
| 101 |
| 102 ----------------------------------------------------- |
| 103 var four = 4; |
| 104 var seasons = 'The $four seasons'; // 'The 4 seasons' |
| 105 ----------------------------------------------------- |
| 106 |
| 107 This is functionally equivalent to the following: |
| 108 |
| 109 ------------------------------------------------------------- |
| 110 var seasons = 'The '.concat(4.toString()).concat(' seasons'); |
| 111 // 'The 4 seasons' |
| 112 ------------------------------------------------------------- |
| 113 |
| 114 You should consider implementing a `toString()` method for user-defined |
| 115 objects. Here's what happens if you don't: |
| 116 |
| 117 ------------------------------------------------------- |
| 118 class Point { |
| 119 num x, y; |
| 120 Point(this.x, this.y); |
| 121 } |
| 122 |
| 123 var point = new Point(3, 4); |
| 124 print('Point: $point'); // "Point: Instance of 'Point'" |
| 125 ------------------------------------------------------- |
| 126 |
| 127 Probably not what you wanted. Here is the same example with an explicit |
| 128 `toString()`: |
| 129 |
| 130 ---------------------------------------------- |
| 131 class Point { |
| 132 ... |
| 133 |
| 134 String toString() => 'x: $x, y: $y'; |
| 135 } |
| 136 |
| 137 print('Point: $point'); // 'Point: x: 3, y: 4' |
| 138 ---------------------------------------------- |
| 139 |
| 140 === Handling special characters within strings |
| 141 |
| 142 ==== Problem |
| 143 |
| 144 You want to put newlines, dollar signs, or other special characters in strings. |
| 145 |
| 146 ==== Solution |
| 147 |
| 148 Prefix special characters with a `\`. |
| 149 |
| 150 ------------------------ |
| 151 print(Wile\nCoyote'); |
| 152 // Wile |
| 153 // Coyote |
| 154 ------------------------ |
| 155 |
| 156 ==== Discussion |
| 157 |
| 158 Dart designates a few characters as special, and these can be escaped: |
| 159 |
| 160 * \n for newline, equivalent to \x0A. |
| 161 * \r for carriage return, equivalent to \x0D. |
| 162 * \f for form feed, equivalent to \x0C. |
| 163 * \b for backspace, equivalent to \x08. |
| 164 * \t for tab, equivalent to \x09. |
| 165 * \v for vertical tab, equivalent to \x0B. |
| 166 |
| 167 If you prefer, you can use `\x` or `\u` notation to indicate the special |
| 168 character: |
| 169 |
| 170 ----------------------------------------------------------- |
| 171 print('Wile\x0ACoyote'); // Same as print('Wile\nCoyote') |
| 172 print('Wile\u000ACoyote'); // Same as print('Wile\nCoyote') |
| 173 ----------------------------------------------------------- |
| 174 |
| 175 You can also use `\u{}` notation: |
| 176 |
| 177 ------------------------------------------------------------- |
| 178 print('Wile\u{000A}Coyote'); // same as print('Wile\nCoyote') |
| 179 ------------------------------------------------------------- |
| 180 |
| 181 You can also escape the `$` used in string interpolation: |
| 182 |
| 183 ------------------------------------------------------------------------- |
| 184 var superGenius = 'Wile Coyote'; |
| 185 print('$superGenius and Road Runner'); // 'Wile Coyote and Road Runner' |
| 186 print('\$superGenius and Road Runner'); // '$superGenius and Road Runner' |
| 187 ------------------------------------------------------------------------- |
| 188 |
| 189 If you escape a non-special character, the `\` is ignored: |
| 190 |
| 191 ------------------------------------------- |
| 192 print('Wile \E Coyote'); // 'Wile E Coyote' |
| 193 ------------------------------------------- |
| 194 |
| 195 |
| 196 === Incrementally building a string using a StringBuffer |
| 197 |
| 198 ==== Problem |
| 199 |
| 200 You want to collect string fragments and combine them in an efficient |
| 201 manner. |
| 202 |
| 203 ==== Solution |
| 204 |
| 205 Use a StringBuffer to programmatically generate a string. Consider this code |
| 206 below for assembling a series of urls from fragments: |
| 207 |
| 208 -------------------------------------------------------------------------- |
| 209 var data = [{'scheme': 'https', 'domain': 'news.ycombinator.com'}, |
| 210 {'domain': 'www.google.com'}, |
| 211 {'domain': 'reddit.com', 'path': 'search', 'params': 'q=dart'} |
| 212 ]; |
| 213 |
| 214 String assembleUrlsUsingStringBuffer(entries) { |
| 215 StringBuffer sb = new StringBuffer(); |
| 216 for (final item in entries) { |
| 217 sb.write(item['scheme'] != null ? item['scheme'] : 'http'); |
| 218 sb.write("://"); |
| 219 sb.write(item['domain']); |
| 220 sb.write('/'); |
| 221 sb.write(item['path'] != null ? item['path'] : ''); |
| 222 if (item['params'] != null) { |
| 223 sb.write('?'); |
| 224 sb.write(item['params']); |
| 225 } |
| 226 sb.write('\n'); |
| 227 } |
| 228 return sb.toString(); |
| 229 } |
| 230 |
| 231 // https://news.ycombinator.com/ |
| 232 // http://www.google.com/ |
| 233 // http://reddit.com/search?q=dart |
| 234 -------------------------------------------------------------------------- |
| 235 |
| 236 A StringBuffer collects string fragments, but does not generate a new string |
| 237 until `toString()` is called. |
| 238 |
| 239 ==== Discussion |
| 240 |
| 241 Using a StringBuffer is vastly more efficient than concatenating fragments |
| 242 at each step: Consider this rewrite of the above code: |
| 243 |
| 244 ------------------------------------------------------------------------- |
| 245 String assembleUrlsUsingConcat(entries) { |
| 246 var urls = ''; |
| 247 for (final item in entries) { |
| 248 urls = urls.concat(item['scheme'] != null ? item['scheme'] : 'http'); |
| 249 urls = urls.concat("://"); |
| 250 urls = urls.concat(item['domain']); |
| 251 urls = urls.concat('/'); |
| 252 urls = urls.concat(item['path'] != null ? item['path'] : ''); |
| 253 if (item['params'] != null) { |
| 254 urls = urls.concat('?'); |
| 255 urls = urls.concat(item['params']); |
| 256 } |
| 257 urls = urls.concat('\n'); |
| 258 } |
| 259 return urls; |
| 260 } |
| 261 ------------------------------------------------------------------------- |
| 262 |
| 263 This approach produces the exact same result, but incurs the cost of |
| 264 joining strings multiple times. |
| 265 |
| 266 See the _Concatenating Strings_ recipe for a description of `concat()`. |
| 267 |
| 268 ===== Other StringBuffer methods |
| 269 |
| 270 In addition to `write()`, the StringBuffer class provides methods to |
| 271 write a list of strings (`writeAll()`), write a numerical character code |
| 272 (`writeCharCode()`), write with an added newline (`writeln()`), and |
| 273 more. The example below shows how to use these methods: |
| 274 |
| 275 ------------------------------------------------------------------------------- |
| 276 var sb = new StringBuffer(); |
| 277 sb.writeln('The Beatles:'); |
| 278 sb.writeAll(['John, ', 'Paul, ', 'George, and Ringo']); |
| 279 sb.writeCharCode(33); // charCode for '!'. |
| 280 var beatles = sb.toString(); // 'The Beatles:\nJohn, Paul, George, and Ringo!' |
| 281 ------------------------------------------------------------------------------- |
| 282 |
| 283 |
| 284 === Determining whether a string is empty |
| 285 |
| 286 ==== Problem |
| 287 |
| 288 You want to know whether a string is empty. You tried `if (string) {...}`, but |
| 289 that did not work. |
| 290 |
| 291 ==== Solution |
| 292 |
| 293 Use `string.isEmpty`: |
| 294 |
| 295 ----------------------------------- |
| 296 var emptyString = ''; |
| 297 print(emptyString.isEmpty); // true |
| 298 ----------------------------------- |
| 299 |
| 300 You can also just use `==`: |
| 301 |
| 302 --------------------------------------------------- |
| 303 if (string == '') {...} // True if string is empty. |
| 304 --------------------------------------------------- |
| 305 |
| 306 A string with a space is not empty: |
| 307 |
| 308 ----------------------- |
| 309 var space = ' '; |
| 310 print(space.isEmpty); // false |
| 311 ----------------------- |
| 312 |
| 313 ==== Discussion |
| 314 |
| 315 Don't use `if (string)` to test the emptiness of a string. In Dart, all objects |
| 316 except the boolean true evaluate to false, so `if(string)` is always false. You |
| 317 will see a warning in the editor if you use an 'if' statement with a non-boolean |
| 318 in checked mode. |
| 319 |
| 320 |
| 321 === Removing leading and trailing whitespace |
| 322 |
| 323 ==== Problem |
| 324 |
| 325 You want to remove spaces, tabs, and other whitespace from the beginning and |
| 326 end of strings. |
| 327 |
| 328 ==== Solution |
| 329 |
| 330 Use `string.trim()`: |
| 331 |
| 332 ----------------------------------------------------------------- |
| 333 var space = '\n\r\f\t\v'; // A variety of space characters. |
| 334 var string = '$space X $space'; |
| 335 var newString = string.trim(); // 'X' |
| 336 ----------------------------------------------------------------- |
| 337 |
| 338 The String class has no methods to remove only leading or only trailing |
| 339 whitespace. You can always use a RegExp. |
| 340 |
| 341 Remove only leading whitespace: |
| 342 |
| 343 ------------------------------------------------------------------------------- |
| 344 var newString = string.replaceFirst(new RegExp(r'^\s+'), ''); // 'X \n\r\f\t\v' |
| 345 ------------------------------------------------------------------------------- |
| 346 |
| 347 Remove only trailing whitespace: |
| 348 |
| 349 ------------------------------------------------------------------------------- |
| 350 var newString = string.replaceFirst(new RegExp(r'\s+$'), ''); // '\n\r\f\t\v X' |
| 351 ------------------------------------------------------------------------------- |
| 352 |
| 353 |
| 354 === Changing string case |
| 355 |
| 356 ==== Problem |
| 357 |
| 358 You want to change the case of strings. |
| 359 |
| 360 ==== Solution |
| 361 |
| 362 Use String's `toUpperCase()` and `toLowerCase()` methods: |
| 363 |
| 364 ---------------------------------------------------------------------- |
| 365 var theOneILove = 'I love Lucy'; |
| 366 theOneILove.toUpperCase(); // 'I LOVE LUCY!' |
| 367 theOneILove.toLowerCase(); // 'i love lucy!' |
| 368 |
| 369 // Zeus in modern Greek. |
| 370 var zeus = '\u0394\u03af\u03b1\u03c2'; // 'Δίας' |
| 371 zeus.toUpperCase(); // 'ΔΊΑΣ' |
| 372 |
| 373 var resume = '\u0052\u00e9\u0073\u0075\u006d\u00e9'; // 'Résumé' |
| 374 resume.toLowerCase(); // 'résumé' |
| 375 ---------------------------------------------------------------------- |
| 376 |
| 377 The `toUpperCase()` and `toLowerCase()` methods don't affect the characters of |
| 378 scripts such as Devanagri that don't have distinct letter cases. |
| 379 |
| 380 ------------------------------------------------------------------------ |
| 381 var chickenKebab = '\u091a\u093f\u0915\u0928 \u0915\u092c\u093e\u092c'; |
| 382 // 'चिकन कबाब' (in Devanagari) |
| 383 chickenKebab.toLowerCase(); // 'चिकन कबाब' |
| 384 chickenKebab.toUpperCase(); // 'चिकन कबाब' |
| 385 ------------------------------------------------------------------------ |
| 386 |
| 387 If a character's case does not change when using `toUpperCase()` and |
| 388 `toLowerCase()`, it is most likely because the character only has one |
| 389 form. |
| 390 |
| 391 |
| 392 === Handling extended characters that are composed of multiple code units |
| 393 |
| 394 ==== Problem |
| 395 |
| 396 You want to use emoticons and other special symbols that don't fit into 16 |
| 397 bits. How can you create such strings and use them correctly in your code? |
| 398 |
| 399 ==== Solution |
| 400 |
| 401 You can create an extended character using `'\u{}'` syntax: |
| 402 |
| 403 ------------------------------ |
| 404 var clef = '\u{1F3BC}'; // 🎼 |
| 405 ------------------------------ |
| 406 |
| 407 ==== Discussion |
| 408 |
| 409 Most UTF-16 strings are stored as two-byte (16 bit) code sequences. |
| 410 Since two bytes can only contain the 65,536 characters in the 0x0 to 0xFFFF |
| 411 range, a pair of strings is used to store values in the 0x10000 to 0x10FFFF |
| 412 range. These strings only have semantic meaning as a pair. Individually, they |
| 413 are invalid UTF-16 strings. The term 'surrogate pair' is often used to |
| 414 describe these strings. |
| 415 |
| 416 The clef glyph `'\u{1F3BC}'` is composed of the `'\uD83C'` and `'\uDFBC'` |
| 417 surrogate pair. |
| 418 |
| 419 You can get an extended string's surrogate pair through its `codeUnits` |
| 420 property: |
| 421 |
| 422 ------------------------------------------------------------------------------- |
| 423 clef.codeUnits.map((codeUnit) => codeUnit.toRadixString(16)); |
| 424 // ['d83c', 'dfbc'] |
| 425 ------------------------------------------------------------------------------- |
| 426 |
| 427 Accessing a surrogate pair member leads to errors, and you should avoid |
| 428 properties and methods that expose it: |
| 429 |
| 430 ------------------------------------------------------------------------------- |
| 431 print('\ud83c'); // Error: '\ud83c' is not a valid string. |
| 432 print('\udfbc'); // Error: '\udfbc' is not a valid string either. |
| 433 clef.split()[1]; // Error: accessing half of a surrogate pair. |
| 434 print(clef[i];) // Again, error: accessing half of a surrogate pair. |
| 435 ------------------------------------------------------------------------------- |
| 436 |
| 437 When dealing with strings containing extended characters, you should use the |
| 438 `runes` getter. |
| 439 |
| 440 To get the string's length, use `string.runes.length`. Don't use |
| 441 `string.length`: |
| 442 |
| 443 ------------------------------------------------------------------------------- |
| 444 print(clef.runes.length); // 1 |
| 445 print(clef.length); // 2 |
| 446 print(clef.codeUnits.length); // 2 |
| 447 ------------------------------------------------------------------------------- |
| 448 |
| 449 To get an individual character or its numeric equivalent, index the rune list: |
| 450 |
| 451 -------------------------------------------------------------- |
| 452 print(clef.runes.toList()[0]); // 127932 ('\u{1F3BC}') |
| 453 -------------------------------------------------------------- |
| 454 |
| 455 To get the string's characters as a list, map the string runes: |
| 456 |
| 457 --------------------------------------------------------------------------- |
| 458 var clef = '\u{1F3BC}'; // 🎼 |
| 459 var title = '$clef list:' |
| 460 print(subject.runes.map((rune) => new String.fromCharCode(rune)).toList()); |
| 461 // ['🎼', ' ', 'l', 'i', 's', 't', ':'] |
| 462 --------------------------------------------------------------------------- |
| 463 |
| 464 |
| 465 === Converting between characters and numerical codes |
| 466 |
| 467 ==== Problem |
| 468 |
| 469 You want to convert string characters into numerical codes and vice versa. |
| 470 You want to do this because sometimes you need to compare characters in a string |
| 471 to numerical values coming from another source. Or, maybe you want to split a |
| 472 string and then operate on each character. |
| 473 |
| 474 ==== Solution |
| 475 |
| 476 Use the `runes` getter to get a string's code points: |
| 477 |
| 478 ----------------------------------------------------------------------------- |
| 479 'Dart'.runes.toList(); // [68, 97, 114, 116] |
| 480 |
| 481 var smileyFace = '\u263A'; // ☺ |
| 482 print(smileyFace.runes.toList()); // [9786], (equivalent to ['\u263A']). |
| 483 |
| 484 var clef = '\u{1F3BC}'; // 🎼 |
| 485 print(clef.runes.toList()); // [127932], (equivalent to ['\u{1F3BC}']). |
| 486 ----------------------------------------------------------------------------- |
| 487 |
| 488 Use `string.codeUnits` to get a string's UTF-16 code units: |
| 489 |
| 490 ---------------------------------------------------- |
| 491 'Dart'.codeUnits.toList(); // [68, 97, 114, 116] |
| 492 smileyFace.codeUnits.toList(); // [9786] |
| 493 clef.codeUnits.toList(); // [55356, 57276] |
| 494 ---------------------------------------------------- |
| 495 |
| 496 ===== Using codeUnitAt() to get individual code units |
| 497 |
| 498 To get the code unit at a particular index, use `codeUnitAt()`: |
| 499 |
| 500 ---------------------------------------------------------------------- |
| 501 'Dart'.codeUnitAt(0); // 68 |
| 502 smileyFace.codeUnitAt(0); // 9786 (the decimal value of '\u263A') |
| 503 clef.codeUnitAt(0); // 55356 (does not represent a legal string) |
| 504 ---------------------------------------------------------------------- |
| 505 |
| 506 ==== Converting numerical codes to strings |
| 507 |
| 508 You can generate a new string from numerical codes using the factory |
| 509 `String.fromCharCodes(charCodes)`. You can pass either runes or code units and |
| 510 `String.fromCharCodes(charCodes)` can tell the difference and do the right |
| 511 thing automatically: |
| 512 |
| 513 ------------------------------------------------------------------------------- |
| 514 print(new String.fromCharCodes([68, 97, 114, 116])); // 'Dart' |
| 515 |
| 516 print(new String.fromCharCodes([73, 32, 9825, 32, 76, 117, 99, 121])); |
| 517 // 'I ♡ Lucy' |
| 518 |
| 519 // Passing code units representing the surrogate pair. |
| 520 print(new String.fromCharCodes([55356, 57276])); // 🎼 |
| 521 |
| 522 // Passing runes. |
| 523 print(new String.fromCharCodes([127932])); // 🎼 |
| 524 ------------------------------------------------------------------------------- |
| 525 |
| 526 You can use the `String.fromCharCode()` factory to convert a single rune |
| 527 or code unit to a string: |
| 528 |
| 529 --------------------------------------- |
| 530 new String.fromCharCode(68); // 'D' |
| 531 new String.fromCharCode(9786); // ☺ |
| 532 new String.fromCharCode(127932); // 🎼 |
| 533 --------------------------------------- |
| 534 |
| 535 Creating a string with only one half of a surrogate pair is permitted, |
| 536 but not recommended. |
| 537 |
| 538 |
| 539 === Calculating the length of a string |
| 540 |
| 541 ==== Problem |
| 542 |
| 543 You want to get the length of a string, but are not sure how to calculate the |
| 544 length correctly when working with variable length Unicode characters. |
| 545 |
| 546 ==== Solution |
| 547 |
| 548 Use `string.runes.length` to get the number of characters in a string. |
| 549 |
| 550 ----------------------------------------- |
| 551 print('I love music'.runes.length); // 12 |
| 552 ----------------------------------------- |
| 553 |
| 554 You can safely use `string.runes.length` to get the length of strings that |
| 555 contain extended characters: |
| 556 |
| 557 ----------------------------------------- |
| 558 var clef = '\u{1F3BC}'; // 🎼 |
| 559 var subject = '$clef list:'; // |
| 560 var music = 'I $hearts $clef'; // 'I ♡ 🎼 ' |
| 561 |
| 562 clef.runes.length; // 1 |
| 563 music.runes.length // 5 |
| 564 ----------------------------------------- |
| 565 |
| 566 ==== Discussion |
| 567 |
| 568 You can directly use a string's `length` property (minus `runes`). This returns |
| 569 the string's code unit length. Using `string.length` produces the same length |
| 570 as `string.runes.length` for most unicode characters. |
| 571 |
| 572 For extended characters, the code unit length is one more than the rune |
| 573 length: |
| 574 |
| 575 ------------------------------------------ |
| 576 clef.length; // 2 |
| 577 |
| 578 var music = 'I $hearts $clef'; // 'I ♡ 🎼 ' |
| 579 music.length; // 6 |
| 580 ------------------------------------------ |
| 581 |
| 582 Unless you specifically need the code unit length of a string, use |
| 583 `string.runes.length`. |
| 584 |
| 585 ===== Working with combined characters |
| 586 |
| 587 It is tempting to brush aside the complexity involved in dealing with runes and |
| 588 code units and base the length of the string on the number of characters it |
| 589 appears to have. Anyone can tell that 'Dart' has four characters, and 'Amelié' |
| 590 has six, right? Almost. The length of 'Dart' is indeed four, but the length of |
| 591 'Amelié' depends on how that string was constructed: |
| 592 |
| 593 --------------------------------------------------- |
| 594 var name = 'Ameli\u00E9'; // 'Amelié' |
| 595 var anotherName = 'Ameli\u0065\u0301'; // 'Amelié' |
| 596 print(name.length); // 6 |
| 597 print(anotherName.length); // 7 |
| 598 --------------------------------------------------- |
| 599 |
| 600 Both `name` and `anotherName` return strings that look the same, but where |
| 601 the 'é' is constructed using a different number of runes. This makes it |
| 602 impossible to know the length of these strings by just looking at them. |
| 603 |
| 604 |
| 605 === Processing a string one character at a time |
| 606 |
| 607 ==== Problem |
| 608 |
| 609 You want to do something with each character in a string. |
| 610 |
| 611 ==== Solution |
| 612 |
| 613 Map the results of calling `string.split('')`: |
| 614 |
| 615 ---------------------------------------------------------- |
| 616 var lang= 'Dart'; |
| 617 |
| 618 // ['*D*', '*a*', '*r*', '*t*'] |
| 619 print(lang.split('').map((char) => '*${char}*').toList()); |
| 620 |
| 621 var smileyFace = '\u263A'; |
| 622 var happy = 'I am $smileyFace'; |
| 623 print(happy.split('')); // ['I', ' ', 'a', 'm', ' ', '☺'] |
| 624 ---------------------------------------------------------- |
| 625 |
| 626 |
| 627 Or, loop over the characters of a string: |
| 628 |
| 629 -------------------------------------------- |
| 630 var list = []; |
| 631 for(var i = 0; i < lang.length; i++) { |
| 632 list.add('*${lang[i]}*'); |
| 633 } |
| 634 |
| 635 print(list); // ['*D*', '*a*', '*r*', '*t*'] |
| 636 -------------------------------------------- |
| 637 |
| 638 Or, map the string runes: |
| 639 |
| 640 ------------------------------------------------------------------------ |
| 641 // ['*D*', '*a*', '*r*', '*t*'] |
| 642 var charList = "Dart".runes.map((rune) { |
| 643 return '*${new String.fromCharCode(rune)}*').toList(); |
| 644 }); |
| 645 |
| 646 // [[73, 'I'], [32, ' '], [97, 'a'], [109, 'm'], [32, ' '], [9786, '☺']] |
| 647 var runeList = happy.runes.map((rune) { |
| 648 return [rune, new String.fromCharCode(rune)]).toList(); |
| 649 }); |
| 650 ------------------------------------------------------------------------ |
| 651 |
| 652 When working with extended characters, you should always map the string runes. |
| 653 Don't use `split('')` and avoid indexing an extended string. See the _Handling |
| 654 extended characters that are composed of multiple code units_ recipe for |
| 655 special considerations when working with extended strings. |
| 656 |
| 657 |
| 658 === Splitting a string into substrings |
| 659 |
| 660 ==== Problem |
| 661 |
| 662 You want to split a string into substrings using a delimiter or a pattern. |
| 663 |
| 664 ==== Solution |
| 665 |
| 666 Use the `split()` method with a string or a RegExp as an argument. |
| 667 |
| 668 ------------------------------------- |
| 669 var smileyFace = '\u263A'; |
| 670 var happy = 'I am $smileyFace'; |
| 671 happy.split(' '); // ['I', 'am', '☺'] |
| 672 ------------------------------------- |
| 673 |
| 674 Here is an example of using `split()` with a RegExp: |
| 675 |
| 676 -------------------------------------------------------------- |
| 677 var nums = '2/7 3 4/5 3~/5'; |
| 678 var numsRegExp = new RegExp(r'(\s|/|~/)'); |
| 679 nums.split(numsRegExp); // ['2', '7', '3', '4', '5', '3', '5'] |
| 680 -------------------------------------------------------------- |
| 681 |
| 682 In the code above, the string `nums` contains various numbers, some of which |
| 683 are expressed as fractions or as int-divisions. A RegExp splits the string to |
| 684 extract just the numbers. |
| 685 |
| 686 You can perform operations on the matched and unmatched portions of a string |
| 687 when using `split()` with a RegExp: |
| 688 |
| 689 ---------------------------------------------------------------- |
| 690 var phrase = 'Eats SHOOTS leaves'; |
| 691 |
| 692 var newPhrase = phrase.splitMapJoin((new RegExp(r'SHOOTS')), |
| 693 onMatch: (m) => '*${m.group(0).toLowerCase()}*', |
| 694 onNonMatch: (n) => n.toUpperCase()); |
| 695 |
| 696 print(newPhrase); // 'EATS *shoots* LEAVES' |
| 697 |
| 698 ---------------------------------------------------------------- |
| 699 |
| 700 The RegExp matches the middle word ('SHOOTS'). A pair of callbacks are |
| 701 registered to transform the matched and unmatched substrings before the |
| 702 substrings are joined together again. |
| 703 |
| 704 |
| 705 === Determining whether a string contains another string |
| 706 |
| 707 ==== Problem |
| 708 |
| 709 You want to find out whether a string is the substring of another string. |
| 710 |
| 711 ==== Solution |
| 712 |
| 713 Use `string.contains()`: |
| 714 |
| 715 --------------------------------------------- |
| 716 var fact = 'Dart strings are immutable'; |
| 717 print(fact.contains('immutable')); // True. |
| 718 --------------------------------------------- |
| 719 |
| 720 You can use a second argument to specify where in the string to start looking: |
| 721 |
| 722 ----------------------------------------- |
| 723 print(fact.contains('Dart', 2)); // False |
| 724 ----------------------------------------- |
| 725 |
| 726 ==== Discussion |
| 727 |
| 728 The String class provides a couple of shortcuts for testing whether a |
| 729 string is a substring of another: |
| 730 |
| 731 ------------------------------------------ |
| 732 print(string.startsWith('Dart')); // True. |
| 733 print(string.endsWith('e')); // True. |
| 734 ------------------------------------------ |
| 735 |
| 736 You can also use `string.indexOf()`, which returns -1 if the substring |
| 737 is not found within a string, and otherwise returns the matching index: |
| 738 |
| 739 --------------------------------------------------------------------------- |
| 740 var found = string.indexOf('art') != -1; // True, `art` is found in `Dart`. |
| 741 --------------------------------------------------------------------------- |
| 742 |
| 743 You can also use a RegExp and `hasMatch()`: |
| 744 |
| 745 ------------------------------------------------------------------------ |
| 746 var found = new RegExp(r'ar[et]').hasMatch(string); |
| 747 // True, 'art' and 'are' match. |
| 748 ------------------------------------------------------------------------ |
| 749 |
| 750 === Finding matches of a RegExp pattern in a string |
| 751 |
| 752 ==== Problem |
| 753 |
| 754 You want to use RegExp to match a pattern in a string, and want to be |
| 755 able to access the matches. |
| 756 |
| 757 ==== Solution |
| 758 |
| 759 Construct a regular expression using the RegExp class, and find matches |
| 760 using the `allMatches()` method: |
| 761 |
| 762 ------------------------------------------------------------------------- |
| 763 var neverEatingThat = 'Not with a fox, not in a box'; |
| 764 var regExp = new RegExp(r'[fb]ox'); |
| 765 List matches = regExp.allMatches(neverEatingThat); |
| 766 print(matches.map((match) => match.group(0)).toList()); // ['fox', 'box'] |
| 767 ------------------------------------------------------------------------- |
| 768 |
| 769 ==== Discussion |
| 770 |
| 771 You can query the object returned by `allMatches()` to find out the |
| 772 number of matches: |
| 773 |
| 774 ----------------------------------------- |
| 775 var howManyMatches = matches.length; // 2 |
| 776 ----------------------------------------- |
| 777 |
| 778 To find the first match, use `firstMatch()`: |
| 779 |
| 780 ---------------------------------------------------------------------- |
| 781 var firstMatch = RegExp.firstMatch(neverEatingThat).group(0); // 'fox' |
| 782 ---------------------------------------------------------------------- |
| 783 |
| 784 To directly get the matched string, use `stringMatch()`: |
| 785 |
| 786 ------------------------------------------------------------ |
| 787 print(regExp.stringMatch(neverEatingThat)); // 'fox' |
| 788 print(regExp.stringMatch('I like bagels and lox')); // null |
| 789 ------------------------------------------------------------ |
| 790 |
| 791 === Substituting strings based on RegExp matches |
| 792 |
| 793 ==== Problem |
| 794 |
| 795 You want to match substrings within a string and make substitutions |
| 796 based on the matches. |
| 797 |
| 798 ==== Solution |
| 799 |
| 800 Construct a regular expression using the RegExp class and make |
| 801 replacements using `replaceAll()` method: |
| 802 |
| 803 ------------------------------------------------------------------------- |
| 804 var resume = 'resume'.replaceAll(new RegExp(r'e'), '\u00E9'); // 'résumé' |
| 805 ------------------------------------------------------------------------- |
| 806 |
| 807 If you want to replace just the first match, use `replaceFirst()`: |
| 808 |
| 809 ----------------------------------------------------------------------- |
| 810 // Replace the first match of one or more zeros with an empty string. |
| 811 var smallNum = '0.0001'.replaceFirst(new RegExp(r'0+'), ''); // '.0001' |
| 812 ----------------------------------------------------------------------- |
| 813 |
| 814 You can use `replaceAllMatched()` to register a function that modifies the |
| 815 matches: |
| 816 |
| 817 --------------------------------------------------------- |
| 818 var heart = '\u2661'; // '♡' |
| 819 var string = 'I like Ike but I $heart Lucy'; |
| 820 var regExp = new RegExp(r'[A-Z]\w+'); |
| 821 var newString = string.replaceAllMapped(regExp, (match) { |
| 822 return match.group(0).toUpperCase() |
| 823 }); |
| 824 print(newString); // 'I like IKE but I ♡ LUCY' |
| 825 --------------------------------------------------------- |
OLD | NEW |