| Index: book.asciidoc
|
| diff --git a/book.asciidoc b/book.asciidoc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..89494a500d0e3328be25d28a69f843c0561461ec
|
| --- /dev/null
|
| +++ b/book.asciidoc
|
| @@ -0,0 +1,825 @@
|
| += Dart Cookbook
|
| +:author: Shailen Tuli
|
| +:encoding: UTF-8
|
| +
|
| +== Strings
|
| +
|
| +=== Concatenating Strings
|
| +
|
| +==== Problem
|
| +
|
| +You want to concatenate strings in Dart. You tried using `+`, but that
|
| +resulted in an error.
|
| +
|
| +==== Solution
|
| +
|
| +Use adjacent string literals:
|
| +
|
| +--------------------------------------------------
|
| +var fact = 'Dart' 'is' ' fun!'; // 'Dart is fun!'
|
| +--------------------------------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +Adjacent literals work over multiple lines:
|
| +
|
| +-------------------------
|
| +var fact = 'Dart'
|
| +'is'
|
| +'fun!'; // 'Dart is fun!'
|
| +-------------------------
|
| +
|
| +They also work when using multiline strings:
|
| +
|
| +----------------------------------------
|
| +var lunch = '''Peanut
|
| +butter'''
|
| +'''and
|
| +jelly'''; // 'Peanut\nbutter and\njelly'
|
| +----------------------------------------
|
| +
|
| +You can concatenate adjacent single line literals with multiline
|
| +strings:
|
| +
|
| +--------------------------------------
|
| +var funnyGuys = 'Dewey ' 'Cheatem'
|
| +''' and
|
| +Howe'''; // 'Dewey Cheatem and\n Howe'
|
| +--------------------------------------
|
| +
|
| +===== Alternatives to adjacent string literals
|
| +
|
| +You can use the `concat()` method on a string to concatenate it to
|
| +another string:
|
| +
|
| +---------------------------------------------------
|
| +var film = filmToWatch();
|
| +film = film.concat('\n'); // 'The Big Lebowski\n'
|
| +---------------------------------------------------
|
| +
|
| +Because `concat()` creates a new string every time it is invoked, a long
|
| +chain of `concat()` s can be expensive. Avoid those. Use a StringBuffer
|
| +instead (see _Incrementally building a string efficiently using a
|
| +StringBuffer_, below).
|
| +
|
| +Use `join()` to combine a sequence of strings:
|
| +
|
| +-----------------------------------------------------------------------
|
| +var film = ['The', 'Big', 'Lebowski']).join(' '); // 'The Big Lebowski'
|
| +-----------------------------------------------------------------------
|
| +
|
| +You can also use string interpolation to concatenate strings (see
|
| +_Interpolating expressions inside strings_, below).
|
| +
|
| +=== Interpolating expressions inside strings
|
| +
|
| +==== Problem
|
| +
|
| +You want to embed Dart code inside strings.
|
| +
|
| +==== Solution
|
| +
|
| +You can put the value of an expression inside a string by using
|
| +$\{expression}.
|
| +
|
| +----------------------------------------------------------------------
|
| +var favFood = 'sushi';
|
| +var whatDoILove = 'I love ${favFood.toUpperCase()}'; // 'I love SUSHI'
|
| +----------------------------------------------------------------------
|
| +
|
| +You can skip the \{} if the expression is an identifier:
|
| +
|
| +------------------------------------------------------
|
| +var whatDoILove = 'I love $favFood'; // 'I love sushi'
|
| +------------------------------------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +An interpolated string, `'string ${expression}` is equivalent to the
|
| +concatenation of the strings `string` and `expression.toString()`.
|
| +Consider this code:
|
| +
|
| +-----------------------------------------------------
|
| +var four = 4;
|
| +var seasons = 'The $four seasons'; // 'The 4 seasons'
|
| +-----------------------------------------------------
|
| +
|
| +This is functionally equivalent to the following:
|
| +
|
| +-------------------------------------------------------------
|
| +var seasons = 'The '.concat(4.toString()).concat(' seasons');
|
| +// 'The 4 seasons'
|
| +-------------------------------------------------------------
|
| +
|
| +You should consider implementing a `toString()` method for user-defined
|
| +objects. Here's what happens if you don't:
|
| +
|
| +-------------------------------------------------------
|
| +class Point {
|
| + num x, y;
|
| + Point(this.x, this.y);
|
| +}
|
| +
|
| +var point = new Point(3, 4);
|
| +print('Point: $point'); // "Point: Instance of 'Point'"
|
| +-------------------------------------------------------
|
| +
|
| +Probably not what you wanted. Here is the same example with an explicit
|
| +`toString()`:
|
| +
|
| +----------------------------------------------
|
| +class Point {
|
| + ...
|
| +
|
| + String toString() => 'x: $x, y: $y';
|
| +}
|
| +
|
| +print('Point: $point'); // 'Point: x: 3, y: 4'
|
| +----------------------------------------------
|
| +
|
| +=== Handling special characters within strings
|
| +
|
| +==== Problem
|
| +
|
| +You want to put newlines, dollar signs, or other special characters in strings.
|
| +
|
| +==== Solution
|
| +
|
| +Prefix special characters with a `\`.
|
| +
|
| +------------------------
|
| + print(Wile\nCoyote');
|
| + // Wile
|
| + // Coyote
|
| +------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +Dart designates a few characters as special, and these can be escaped:
|
| +
|
| +* \n for newline, equivalent to \x0A.
|
| +* \r for carriage return, equivalent to \x0D.
|
| +* \f for form feed, equivalent to \x0C.
|
| +* \b for backspace, equivalent to \x08.
|
| +* \t for tab, equivalent to \x09.
|
| +* \v for vertical tab, equivalent to \x0B.
|
| +
|
| +If you prefer, you can use `\x` or `\u` notation to indicate the special
|
| +character:
|
| +
|
| +-----------------------------------------------------------
|
| +print('Wile\x0ACoyote'); // Same as print('Wile\nCoyote')
|
| +print('Wile\u000ACoyote'); // Same as print('Wile\nCoyote')
|
| +-----------------------------------------------------------
|
| +
|
| +You can also use `\u{}` notation:
|
| +
|
| +-------------------------------------------------------------
|
| +print('Wile\u{000A}Coyote'); // same as print('Wile\nCoyote')
|
| +-------------------------------------------------------------
|
| +
|
| +You can also escape the `$` used in string interpolation:
|
| +
|
| +-------------------------------------------------------------------------
|
| +var superGenius = 'Wile Coyote';
|
| +print('$superGenius and Road Runner'); // 'Wile Coyote and Road Runner'
|
| +print('\$superGenius and Road Runner'); // '$superGenius and Road Runner'
|
| +-------------------------------------------------------------------------
|
| +
|
| +If you escape a non-special character, the `\` is ignored:
|
| +
|
| +-------------------------------------------
|
| +print('Wile \E Coyote'); // 'Wile E Coyote'
|
| +-------------------------------------------
|
| +
|
| +
|
| +=== Incrementally building a string using a StringBuffer
|
| +
|
| +==== Problem
|
| +
|
| +You want to collect string fragments and combine them in an efficient
|
| +manner.
|
| +
|
| +==== Solution
|
| +
|
| +Use a StringBuffer to programmatically generate a string. Consider this code
|
| +below for assembling a series of urls from fragments:
|
| +
|
| +--------------------------------------------------------------------------
|
| +var data = [{'scheme': 'https', 'domain': 'news.ycombinator.com'},
|
| + {'domain': 'www.google.com'},
|
| + {'domain': 'reddit.com', 'path': 'search', 'params': 'q=dart'}
|
| + ];
|
| +
|
| +String assembleUrlsUsingStringBuffer(entries) {
|
| + StringBuffer sb = new StringBuffer();
|
| + for (final item in entries) {
|
| + sb.write(item['scheme'] != null ? item['scheme'] : 'http');
|
| + sb.write("://");
|
| + sb.write(item['domain']);
|
| + sb.write('/');
|
| + sb.write(item['path'] != null ? item['path'] : '');
|
| + if (item['params'] != null) {
|
| + sb.write('?');
|
| + sb.write(item['params']);
|
| + }
|
| + sb.write('\n');
|
| + }
|
| + return sb.toString();
|
| +}
|
| +
|
| +// https://news.ycombinator.com/
|
| +// http://www.google.com/
|
| +// http://reddit.com/search?q=dart
|
| +--------------------------------------------------------------------------
|
| +
|
| +A StringBuffer collects string fragments, but does not generate a new string
|
| +until `toString()` is called.
|
| +
|
| +==== Discussion
|
| +
|
| +Using a StringBuffer is vastly more efficient than concatenating fragments
|
| +at each step: Consider this rewrite of the above code:
|
| +
|
| +-------------------------------------------------------------------------
|
| +String assembleUrlsUsingConcat(entries) {
|
| + var urls = '';
|
| + for (final item in entries) {
|
| + urls = urls.concat(item['scheme'] != null ? item['scheme'] : 'http');
|
| + urls = urls.concat("://");
|
| + urls = urls.concat(item['domain']);
|
| + urls = urls.concat('/');
|
| + urls = urls.concat(item['path'] != null ? item['path'] : '');
|
| + if (item['params'] != null) {
|
| + urls = urls.concat('?');
|
| + urls = urls.concat(item['params']);
|
| + }
|
| + urls = urls.concat('\n');
|
| + }
|
| + return urls;
|
| +}
|
| +-------------------------------------------------------------------------
|
| +
|
| +This approach produces the exact same result, but incurs the cost of
|
| +joining strings multiple times.
|
| +
|
| +See the _Concatenating Strings_ recipe for a description of `concat()`.
|
| +
|
| +===== Other StringBuffer methods
|
| +
|
| +In addition to `write()`, the StringBuffer class provides methods to
|
| +write a list of strings (`writeAll()`), write a numerical character code
|
| +(`writeCharCode()`), write with an added newline (`writeln()`), and
|
| +more. The example below shows how to use these methods:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +var sb = new StringBuffer();
|
| +sb.writeln('The Beatles:');
|
| +sb.writeAll(['John, ', 'Paul, ', 'George, and Ringo']);
|
| +sb.writeCharCode(33); // charCode for '!'.
|
| +var beatles = sb.toString(); // 'The Beatles:\nJohn, Paul, George, and Ringo!'
|
| +-------------------------------------------------------------------------------
|
| +
|
| +
|
| +=== Determining whether a string is empty
|
| +
|
| +==== Problem
|
| +
|
| +You want to know whether a string is empty. You tried `if (string) {...}`, but
|
| +that did not work.
|
| +
|
| +==== Solution
|
| +
|
| +Use `string.isEmpty`:
|
| +
|
| +-----------------------------------
|
| +var emptyString = '';
|
| +print(emptyString.isEmpty); // true
|
| +-----------------------------------
|
| +
|
| +You can also just use `==`:
|
| +
|
| +---------------------------------------------------
|
| +if (string == '') {...} // True if string is empty.
|
| +---------------------------------------------------
|
| +
|
| +A string with a space is not empty:
|
| +
|
| +-----------------------
|
| +var space = ' ';
|
| +print(space.isEmpty); // false
|
| +-----------------------
|
| +
|
| +==== Discussion
|
| +
|
| +Don't use `if (string)` to test the emptiness of a string. In Dart, all objects
|
| +except the boolean true evaluate to false, so `if(string)` is always false. You
|
| +will see a warning in the editor if you use an 'if' statement with a non-boolean
|
| +in checked mode.
|
| +
|
| +
|
| +=== Removing leading and trailing whitespace
|
| +
|
| +==== Problem
|
| +
|
| +You want to remove spaces, tabs, and other whitespace from the beginning and
|
| +end of strings.
|
| +
|
| +==== Solution
|
| +
|
| +Use `string.trim()`:
|
| +
|
| +-----------------------------------------------------------------
|
| +var space = '\n\r\f\t\v'; // A variety of space characters.
|
| +var string = '$space X $space';
|
| +var newString = string.trim(); // 'X'
|
| +-----------------------------------------------------------------
|
| +
|
| +The String class has no methods to remove only leading or only trailing
|
| +whitespace. You can always use a RegExp.
|
| +
|
| +Remove only leading whitespace:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +var newString = string.replaceFirst(new RegExp(r'^\s+'), ''); // 'X \n\r\f\t\v'
|
| +-------------------------------------------------------------------------------
|
| +
|
| +Remove only trailing whitespace:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +var newString = string.replaceFirst(new RegExp(r'\s+$'), ''); // '\n\r\f\t\v X'
|
| +-------------------------------------------------------------------------------
|
| +
|
| +
|
| +=== Changing string case
|
| +
|
| +==== Problem
|
| +
|
| +You want to change the case of strings.
|
| +
|
| +==== Solution
|
| +
|
| +Use String's `toUpperCase()` and `toLowerCase()` methods:
|
| +
|
| +----------------------------------------------------------------------
|
| +var theOneILove = 'I love Lucy';
|
| +theOneILove.toUpperCase(); // 'I LOVE LUCY!'
|
| +theOneILove.toLowerCase(); // 'i love lucy!'
|
| +
|
| +// Zeus in modern Greek.
|
| +var zeus = '\u0394\u03af\u03b1\u03c2'; // 'Δίας'
|
| +zeus.toUpperCase(); // 'ΔΊΑΣ'
|
| +
|
| +var resume = '\u0052\u00e9\u0073\u0075\u006d\u00e9'; // 'Résumé'
|
| +resume.toLowerCase(); // 'résumé'
|
| +----------------------------------------------------------------------
|
| +
|
| +The `toUpperCase()` and `toLowerCase()` methods don't affect the characters of
|
| +scripts such as Devanagri that don't have distinct letter cases.
|
| +
|
| +------------------------------------------------------------------------
|
| +var chickenKebab = '\u091a\u093f\u0915\u0928 \u0915\u092c\u093e\u092c';
|
| +// 'चिकन कबाब' (in Devanagari)
|
| +chickenKebab.toLowerCase(); // 'चिकन कबाब'
|
| +chickenKebab.toUpperCase(); // 'चिकन कबाब'
|
| +------------------------------------------------------------------------
|
| +
|
| +If a character's case does not change when using `toUpperCase()` and
|
| +`toLowerCase()`, it is most likely because the character only has one
|
| +form.
|
| +
|
| +
|
| +=== Handling extended characters that are composed of multiple code units
|
| +
|
| +==== Problem
|
| +
|
| +You want to use emoticons and other special symbols that don't fit into 16
|
| +bits. How can you create such strings and use them correctly in your code?
|
| +
|
| +==== Solution
|
| +
|
| +You can create an extended character using `'\u{}'` syntax:
|
| +
|
| +------------------------------
|
| +var clef = '\u{1F3BC}'; // 🎼
|
| +------------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +Most UTF-16 strings are stored as two-byte (16 bit) code sequences.
|
| +Since two bytes can only contain the 65,536 characters in the 0x0 to 0xFFFF
|
| +range, a pair of strings is used to store values in the 0x10000 to 0x10FFFF
|
| +range. These strings only have semantic meaning as a pair. Individually, they
|
| +are invalid UTF-16 strings. The term 'surrogate pair' is often used to
|
| +describe these strings.
|
| +
|
| +The clef glyph `'\u{1F3BC}'` is composed of the `'\uD83C'` and `'\uDFBC'`
|
| +surrogate pair.
|
| +
|
| +You can get an extended string's surrogate pair through its `codeUnits`
|
| +property:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +clef.codeUnits.map((codeUnit) => codeUnit.toRadixString(16));
|
| +// ['d83c', 'dfbc']
|
| +-------------------------------------------------------------------------------
|
| +
|
| +Accessing a surrogate pair member leads to errors, and you should avoid
|
| +properties and methods that expose it:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +print('\ud83c'); // Error: '\ud83c' is not a valid string.
|
| +print('\udfbc'); // Error: '\udfbc' is not a valid string either.
|
| +clef.split()[1]; // Error: accessing half of a surrogate pair.
|
| +print(clef[i];) // Again, error: accessing half of a surrogate pair.
|
| +-------------------------------------------------------------------------------
|
| +
|
| +When dealing with strings containing extended characters, you should use the
|
| +`runes` getter.
|
| +
|
| +To get the string's length, use `string.runes.length`. Don't use
|
| +`string.length`:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +print(clef.runes.length); // 1
|
| +print(clef.length); // 2
|
| +print(clef.codeUnits.length); // 2
|
| +-------------------------------------------------------------------------------
|
| +
|
| +To get an individual character or its numeric equivalent, index the rune list:
|
| +
|
| +--------------------------------------------------------------
|
| +print(clef.runes.toList()[0]); // 127932 ('\u{1F3BC}')
|
| +--------------------------------------------------------------
|
| +
|
| +To get the string's characters as a list, map the string runes:
|
| +
|
| +---------------------------------------------------------------------------
|
| +var clef = '\u{1F3BC}'; // 🎼
|
| +var title = '$clef list:'
|
| +print(subject.runes.map((rune) => new String.fromCharCode(rune)).toList());
|
| +// ['🎼', ' ', 'l', 'i', 's', 't', ':']
|
| +---------------------------------------------------------------------------
|
| +
|
| +
|
| +=== Converting between characters and numerical codes
|
| +
|
| +==== Problem
|
| +
|
| +You want to convert string characters into numerical codes and vice versa.
|
| +You want to do this because sometimes you need to compare characters in a string
|
| +to numerical values coming from another source. Or, maybe you want to split a
|
| +string and then operate on each character.
|
| +
|
| +==== Solution
|
| +
|
| +Use the `runes` getter to get a string's code points:
|
| +
|
| +-----------------------------------------------------------------------------
|
| +'Dart'.runes.toList(); // [68, 97, 114, 116]
|
| +
|
| +var smileyFace = '\u263A'; // ☺
|
| +print(smileyFace.runes.toList()); // [9786], (equivalent to ['\u263A']).
|
| +
|
| +var clef = '\u{1F3BC}'; // 🎼
|
| +print(clef.runes.toList()); // [127932], (equivalent to ['\u{1F3BC}']).
|
| +-----------------------------------------------------------------------------
|
| +
|
| +Use `string.codeUnits` to get a string's UTF-16 code units:
|
| +
|
| +----------------------------------------------------
|
| +'Dart'.codeUnits.toList(); // [68, 97, 114, 116]
|
| +smileyFace.codeUnits.toList(); // [9786]
|
| +clef.codeUnits.toList(); // [55356, 57276]
|
| +----------------------------------------------------
|
| +
|
| +===== Using codeUnitAt() to get individual code units
|
| +
|
| +To get the code unit at a particular index, use `codeUnitAt()`:
|
| +
|
| +----------------------------------------------------------------------
|
| +'Dart'.codeUnitAt(0); // 68
|
| +smileyFace.codeUnitAt(0); // 9786 (the decimal value of '\u263A')
|
| +clef.codeUnitAt(0); // 55356 (does not represent a legal string)
|
| +----------------------------------------------------------------------
|
| +
|
| +==== Converting numerical codes to strings
|
| +
|
| +You can generate a new string from numerical codes using the factory
|
| +`String.fromCharCodes(charCodes)`. You can pass either runes or code units and
|
| +`String.fromCharCodes(charCodes)` can tell the difference and do the right
|
| +thing automatically:
|
| +
|
| +-------------------------------------------------------------------------------
|
| +print(new String.fromCharCodes([68, 97, 114, 116])); // 'Dart'
|
| +
|
| +print(new String.fromCharCodes([73, 32, 9825, 32, 76, 117, 99, 121]));
|
| +// 'I ♡ Lucy'
|
| +
|
| +// Passing code units representing the surrogate pair.
|
| +print(new String.fromCharCodes([55356, 57276])); // 🎼
|
| +
|
| +// Passing runes.
|
| +print(new String.fromCharCodes([127932])); // 🎼
|
| +-------------------------------------------------------------------------------
|
| +
|
| +You can use the `String.fromCharCode()` factory to convert a single rune
|
| +or code unit to a string:
|
| +
|
| +---------------------------------------
|
| +new String.fromCharCode(68); // 'D'
|
| +new String.fromCharCode(9786); // ☺
|
| +new String.fromCharCode(127932); // 🎼
|
| +---------------------------------------
|
| +
|
| +Creating a string with only one half of a surrogate pair is permitted,
|
| +but not recommended.
|
| +
|
| +
|
| +=== Calculating the length of a string
|
| +
|
| +==== Problem
|
| +
|
| +You want to get the length of a string, but are not sure how to calculate the
|
| +length correctly when working with variable length Unicode characters.
|
| +
|
| +==== Solution
|
| +
|
| +Use `string.runes.length` to get the number of characters in a string.
|
| +
|
| +-----------------------------------------
|
| +print('I love music'.runes.length); // 12
|
| +-----------------------------------------
|
| +
|
| +You can safely use `string.runes.length` to get the length of strings that
|
| +contain extended characters:
|
| +
|
| +-----------------------------------------
|
| +var clef = '\u{1F3BC}'; // 🎼
|
| +var subject = '$clef list:'; //
|
| +var music = 'I $hearts $clef'; // 'I ♡ 🎼 '
|
| +
|
| +clef.runes.length; // 1
|
| +music.runes.length // 5
|
| +-----------------------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +You can directly use a string's `length` property (minus `runes`). This returns
|
| +the string's code unit length. Using `string.length` produces the same length
|
| +as `string.runes.length` for most unicode characters.
|
| +
|
| +For extended characters, the code unit length is one more than the rune
|
| +length:
|
| +
|
| +------------------------------------------
|
| +clef.length; // 2
|
| +
|
| +var music = 'I $hearts $clef'; // 'I ♡ 🎼 '
|
| +music.length; // 6
|
| +------------------------------------------
|
| +
|
| +Unless you specifically need the code unit length of a string, use
|
| +`string.runes.length`.
|
| +
|
| +===== Working with combined characters
|
| +
|
| +It is tempting to brush aside the complexity involved in dealing with runes and
|
| +code units and base the length of the string on the number of characters it
|
| +appears to have. Anyone can tell that 'Dart' has four characters, and 'Amelié'
|
| +has six, right? Almost. The length of 'Dart' is indeed four, but the length of
|
| +'Amelié' depends on how that string was constructed:
|
| +
|
| +---------------------------------------------------
|
| +var name = 'Ameli\u00E9'; // 'Amelié'
|
| +var anotherName = 'Ameli\u0065\u0301'; // 'Amelié'
|
| +print(name.length); // 6
|
| +print(anotherName.length); // 7
|
| +---------------------------------------------------
|
| +
|
| +Both `name` and `anotherName` return strings that look the same, but where
|
| +the 'é' is constructed using a different number of runes. This makes it
|
| +impossible to know the length of these strings by just looking at them.
|
| +
|
| +
|
| +=== Processing a string one character at a time
|
| +
|
| +==== Problem
|
| +
|
| +You want to do something with each character in a string.
|
| +
|
| +==== Solution
|
| +
|
| +Map the results of calling `string.split('')`:
|
| +
|
| +----------------------------------------------------------
|
| +var lang= 'Dart';
|
| +
|
| +// ['*D*', '*a*', '*r*', '*t*']
|
| +print(lang.split('').map((char) => '*${char}*').toList());
|
| +
|
| +var smileyFace = '\u263A';
|
| +var happy = 'I am $smileyFace';
|
| +print(happy.split('')); // ['I', ' ', 'a', 'm', ' ', '☺']
|
| +----------------------------------------------------------
|
| +
|
| +
|
| +Or, loop over the characters of a string:
|
| +
|
| +--------------------------------------------
|
| +var list = [];
|
| +for(var i = 0; i < lang.length; i++) {
|
| + list.add('*${lang[i]}*');
|
| +}
|
| +
|
| +print(list); // ['*D*', '*a*', '*r*', '*t*']
|
| +--------------------------------------------
|
| +
|
| +Or, map the string runes:
|
| +
|
| +------------------------------------------------------------------------
|
| +// ['*D*', '*a*', '*r*', '*t*']
|
| +var charList = "Dart".runes.map((rune) {
|
| + return '*${new String.fromCharCode(rune)}*').toList();
|
| +});
|
| +
|
| +// [[73, 'I'], [32, ' '], [97, 'a'], [109, 'm'], [32, ' '], [9786, '☺']]
|
| +var runeList = happy.runes.map((rune) {
|
| + return [rune, new String.fromCharCode(rune)]).toList();
|
| +});
|
| +------------------------------------------------------------------------
|
| +
|
| +When working with extended characters, you should always map the string runes.
|
| +Don't use `split('')` and avoid indexing an extended string. See the _Handling
|
| +extended characters that are composed of multiple code units_ recipe for
|
| +special considerations when working with extended strings.
|
| +
|
| +
|
| +=== Splitting a string into substrings
|
| +
|
| +==== Problem
|
| +
|
| +You want to split a string into substrings using a delimiter or a pattern.
|
| +
|
| +==== Solution
|
| +
|
| +Use the `split()` method with a string or a RegExp as an argument.
|
| +
|
| +-------------------------------------
|
| +var smileyFace = '\u263A';
|
| +var happy = 'I am $smileyFace';
|
| +happy.split(' '); // ['I', 'am', '☺']
|
| +-------------------------------------
|
| +
|
| +Here is an example of using `split()` with a RegExp:
|
| +
|
| +--------------------------------------------------------------
|
| +var nums = '2/7 3 4/5 3~/5';
|
| +var numsRegExp = new RegExp(r'(\s|/|~/)');
|
| +nums.split(numsRegExp); // ['2', '7', '3', '4', '5', '3', '5']
|
| +--------------------------------------------------------------
|
| +
|
| +In the code above, the string `nums` contains various numbers, some of which
|
| +are expressed as fractions or as int-divisions. A RegExp splits the string to
|
| +extract just the numbers.
|
| +
|
| +You can perform operations on the matched and unmatched portions of a string
|
| +when using `split()` with a RegExp:
|
| +
|
| +----------------------------------------------------------------
|
| +var phrase = 'Eats SHOOTS leaves';
|
| +
|
| +var newPhrase = phrase.splitMapJoin((new RegExp(r'SHOOTS')),
|
| + onMatch: (m) => '*${m.group(0).toLowerCase()}*',
|
| + onNonMatch: (n) => n.toUpperCase());
|
| +
|
| +print(newPhrase); // 'EATS *shoots* LEAVES'
|
| +
|
| +----------------------------------------------------------------
|
| +
|
| +The RegExp matches the middle word ('SHOOTS'). A pair of callbacks are
|
| +registered to transform the matched and unmatched substrings before the
|
| +substrings are joined together again.
|
| +
|
| +
|
| +=== Determining whether a string contains another string
|
| +
|
| +==== Problem
|
| +
|
| +You want to find out whether a string is the substring of another string.
|
| +
|
| +==== Solution
|
| +
|
| +Use `string.contains()`:
|
| +
|
| +---------------------------------------------
|
| +var fact = 'Dart strings are immutable';
|
| +print(fact.contains('immutable')); // True.
|
| +---------------------------------------------
|
| +
|
| +You can use a second argument to specify where in the string to start looking:
|
| +
|
| +-----------------------------------------
|
| +print(fact.contains('Dart', 2)); // False
|
| +-----------------------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +The String class provides a couple of shortcuts for testing whether a
|
| +string is a substring of another:
|
| +
|
| +------------------------------------------
|
| +print(string.startsWith('Dart')); // True.
|
| +print(string.endsWith('e')); // True.
|
| +------------------------------------------
|
| +
|
| +You can also use `string.indexOf()`, which returns -1 if the substring
|
| +is not found within a string, and otherwise returns the matching index:
|
| +
|
| +---------------------------------------------------------------------------
|
| +var found = string.indexOf('art') != -1; // True, `art` is found in `Dart`.
|
| +---------------------------------------------------------------------------
|
| +
|
| +You can also use a RegExp and `hasMatch()`:
|
| +
|
| +------------------------------------------------------------------------
|
| +var found = new RegExp(r'ar[et]').hasMatch(string);
|
| +// True, 'art' and 'are' match.
|
| +------------------------------------------------------------------------
|
| +
|
| +=== Finding matches of a RegExp pattern in a string
|
| +
|
| +==== Problem
|
| +
|
| +You want to use RegExp to match a pattern in a string, and want to be
|
| +able to access the matches.
|
| +
|
| +==== Solution
|
| +
|
| +Construct a regular expression using the RegExp class, and find matches
|
| +using the `allMatches()` method:
|
| +
|
| +-------------------------------------------------------------------------
|
| +var neverEatingThat = 'Not with a fox, not in a box';
|
| +var regExp = new RegExp(r'[fb]ox');
|
| +List matches = regExp.allMatches(neverEatingThat);
|
| +print(matches.map((match) => match.group(0)).toList()); // ['fox', 'box']
|
| +-------------------------------------------------------------------------
|
| +
|
| +==== Discussion
|
| +
|
| +You can query the object returned by `allMatches()` to find out the
|
| +number of matches:
|
| +
|
| +-----------------------------------------
|
| +var howManyMatches = matches.length; // 2
|
| +-----------------------------------------
|
| +
|
| +To find the first match, use `firstMatch()`:
|
| +
|
| +----------------------------------------------------------------------
|
| +var firstMatch = RegExp.firstMatch(neverEatingThat).group(0); // 'fox'
|
| +----------------------------------------------------------------------
|
| +
|
| +To directly get the matched string, use `stringMatch()`:
|
| +
|
| +------------------------------------------------------------
|
| +print(regExp.stringMatch(neverEatingThat)); // 'fox'
|
| +print(regExp.stringMatch('I like bagels and lox')); // null
|
| +------------------------------------------------------------
|
| +
|
| +=== Substituting strings based on RegExp matches
|
| +
|
| +==== Problem
|
| +
|
| +You want to match substrings within a string and make substitutions
|
| +based on the matches.
|
| +
|
| +==== Solution
|
| +
|
| +Construct a regular expression using the RegExp class and make
|
| +replacements using `replaceAll()` method:
|
| +
|
| +-------------------------------------------------------------------------
|
| +var resume = 'resume'.replaceAll(new RegExp(r'e'), '\u00E9'); // 'résumé'
|
| +-------------------------------------------------------------------------
|
| +
|
| +If you want to replace just the first match, use `replaceFirst()`:
|
| +
|
| +-----------------------------------------------------------------------
|
| +// Replace the first match of one or more zeros with an empty string.
|
| +var smallNum = '0.0001'.replaceFirst(new RegExp(r'0+'), ''); // '.0001'
|
| +-----------------------------------------------------------------------
|
| +
|
| +You can use `replaceAllMatched()` to register a function that modifies the
|
| +matches:
|
| +
|
| +---------------------------------------------------------
|
| +var heart = '\u2661'; // '♡'
|
| +var string = 'I like Ike but I $heart Lucy';
|
| +var regExp = new RegExp(r'[A-Z]\w+');
|
| +var newString = string.replaceAllMapped(regExp, (match) {
|
| + return match.group(0).toUpperCase()
|
| +});
|
| +print(newString); // 'I like IKE but I ♡ LUCY'
|
| +---------------------------------------------------------
|
|
|