Index: book.html |
diff --git a/book.html b/book.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7262a2b6c8e877083844b09743e01a78225cbf62 |
--- /dev/null |
+++ b/book.html |
@@ -0,0 +1,1551 @@ |
+<!DOCTYPE html> |
+<html lang="en"> |
+<head> |
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
+<meta name="generator" content="AsciiDoc 8.6.8"> |
+<title>Dart Cookbook</title> |
+<style type="text/css"> |
+/* Shared CSS for AsciiDoc xhtml11 and html5 backends */ |
+ |
+/* Default font. */ |
+body { |
+ font-family: Georgia,serif; |
+} |
+ |
+/* Title font. */ |
+h1, h2, h3, h4, h5, h6, |
+div.title, caption.title, |
+thead, p.table.header, |
+#toctitle, |
+#author, #revnumber, #revdate, #revremark, |
+#footer { |
+ font-family: Arial,Helvetica,sans-serif; |
+} |
+ |
+body { |
+ margin: 1em 5% 1em 5%; |
+} |
+ |
+a { |
+ color: blue; |
+ text-decoration: underline; |
+} |
+a:visited { |
+ color: fuchsia; |
+} |
+ |
+em { |
+ font-style: italic; |
+ color: navy; |
+} |
+ |
+strong { |
+ font-weight: bold; |
+ color: #083194; |
+} |
+ |
+h1, h2, h3, h4, h5, h6 { |
+ color: #527bbd; |
+ margin-top: 1.2em; |
+ margin-bottom: 0.5em; |
+ line-height: 1.3; |
+} |
+ |
+h1, h2, h3 { |
+ border-bottom: 2px solid silver; |
+} |
+h2 { |
+ padding-top: 0.5em; |
+} |
+h3 { |
+ float: left; |
+} |
+h3 + * { |
+ clear: left; |
+} |
+h5 { |
+ font-size: 1.0em; |
+} |
+ |
+div.sectionbody { |
+ margin-left: 0; |
+} |
+ |
+hr { |
+ border: 1px solid silver; |
+} |
+ |
+p { |
+ margin-top: 0.5em; |
+ margin-bottom: 0.5em; |
+} |
+ |
+ul, ol, li > p { |
+ margin-top: 0; |
+} |
+ul > li { color: #aaa; } |
+ul > li > * { color: black; } |
+ |
+.monospaced, code, pre { |
+ font-family: "Courier New", Courier, monospace; |
+ font-size: inherit; |
+ color: navy; |
+ padding: 0; |
+ margin: 0; |
+} |
+ |
+ |
+#author { |
+ color: #527bbd; |
+ font-weight: bold; |
+ font-size: 1.1em; |
+} |
+#email { |
+} |
+#revnumber, #revdate, #revremark { |
+} |
+ |
+#footer { |
+ font-size: small; |
+ border-top: 2px solid silver; |
+ padding-top: 0.5em; |
+ margin-top: 4.0em; |
+} |
+#footer-text { |
+ float: left; |
+ padding-bottom: 0.5em; |
+} |
+#footer-badges { |
+ float: right; |
+ padding-bottom: 0.5em; |
+} |
+ |
+#preamble { |
+ margin-top: 1.5em; |
+ margin-bottom: 1.5em; |
+} |
+div.imageblock, div.exampleblock, div.verseblock, |
+div.quoteblock, div.literalblock, div.listingblock, div.sidebarblock, |
+div.admonitionblock { |
+ margin-top: 1.0em; |
+ margin-bottom: 1.5em; |
+} |
+div.admonitionblock { |
+ margin-top: 2.0em; |
+ margin-bottom: 2.0em; |
+ margin-right: 10%; |
+ color: #606060; |
+} |
+ |
+div.content { /* Block element content. */ |
+ padding: 0; |
+} |
+ |
+/* Block element titles. */ |
+div.title, caption.title { |
+ color: #527bbd; |
+ font-weight: bold; |
+ text-align: left; |
+ margin-top: 1.0em; |
+ margin-bottom: 0.5em; |
+} |
+div.title + * { |
+ margin-top: 0; |
+} |
+ |
+td div.title:first-child { |
+ margin-top: 0.0em; |
+} |
+div.content div.title:first-child { |
+ margin-top: 0.0em; |
+} |
+div.content + div.title { |
+ margin-top: 0.0em; |
+} |
+ |
+div.sidebarblock > div.content { |
+ background: #ffffee; |
+ border: 1px solid #dddddd; |
+ border-left: 4px solid #f0f0f0; |
+ padding: 0.5em; |
+} |
+ |
+div.listingblock > div.content { |
+ border: 1px solid #dddddd; |
+ border-left: 5px solid #f0f0f0; |
+ background: #f8f8f8; |
+ padding: 0.5em; |
+} |
+ |
+div.quoteblock, div.verseblock { |
+ padding-left: 1.0em; |
+ margin-left: 1.0em; |
+ margin-right: 10%; |
+ border-left: 5px solid #f0f0f0; |
+ color: #888; |
+} |
+ |
+div.quoteblock > div.attribution { |
+ padding-top: 0.5em; |
+ text-align: right; |
+} |
+ |
+div.verseblock > pre.content { |
+ font-family: inherit; |
+ font-size: inherit; |
+} |
+div.verseblock > div.attribution { |
+ padding-top: 0.75em; |
+ text-align: left; |
+} |
+/* DEPRECATED: Pre version 8.2.7 verse style literal block. */ |
+div.verseblock + div.attribution { |
+ text-align: left; |
+} |
+ |
+div.admonitionblock .icon { |
+ vertical-align: top; |
+ font-size: 1.1em; |
+ font-weight: bold; |
+ text-decoration: underline; |
+ color: #527bbd; |
+ padding-right: 0.5em; |
+} |
+div.admonitionblock td.content { |
+ padding-left: 0.5em; |
+ border-left: 3px solid #dddddd; |
+} |
+ |
+div.exampleblock > div.content { |
+ border-left: 3px solid #dddddd; |
+ padding-left: 0.5em; |
+} |
+ |
+div.imageblock div.content { padding-left: 0; } |
+span.image img { border-style: none; } |
+a.image:visited { color: white; } |
+ |
+dl { |
+ margin-top: 0.8em; |
+ margin-bottom: 0.8em; |
+} |
+dt { |
+ margin-top: 0.5em; |
+ margin-bottom: 0; |
+ font-style: normal; |
+ color: navy; |
+} |
+dd > *:first-child { |
+ margin-top: 0.1em; |
+} |
+ |
+ul, ol { |
+ list-style-position: outside; |
+} |
+ol.arabic { |
+ list-style-type: decimal; |
+} |
+ol.loweralpha { |
+ list-style-type: lower-alpha; |
+} |
+ol.upperalpha { |
+ list-style-type: upper-alpha; |
+} |
+ol.lowerroman { |
+ list-style-type: lower-roman; |
+} |
+ol.upperroman { |
+ list-style-type: upper-roman; |
+} |
+ |
+div.compact ul, div.compact ol, |
+div.compact p, div.compact p, |
+div.compact div, div.compact div { |
+ margin-top: 0.1em; |
+ margin-bottom: 0.1em; |
+} |
+ |
+tfoot { |
+ font-weight: bold; |
+} |
+td > div.verse { |
+ white-space: pre; |
+} |
+ |
+div.hdlist { |
+ margin-top: 0.8em; |
+ margin-bottom: 0.8em; |
+} |
+div.hdlist tr { |
+ padding-bottom: 15px; |
+} |
+dt.hdlist1.strong, td.hdlist1.strong { |
+ font-weight: bold; |
+} |
+td.hdlist1 { |
+ vertical-align: top; |
+ font-style: normal; |
+ padding-right: 0.8em; |
+ color: navy; |
+} |
+td.hdlist2 { |
+ vertical-align: top; |
+} |
+div.hdlist.compact tr { |
+ margin: 0; |
+ padding-bottom: 0; |
+} |
+ |
+.comment { |
+ background: yellow; |
+} |
+ |
+.footnote, .footnoteref { |
+ font-size: 0.8em; |
+} |
+ |
+span.footnote, span.footnoteref { |
+ vertical-align: super; |
+} |
+ |
+#footnotes { |
+ margin: 20px 0 20px 0; |
+ padding: 7px 0 0 0; |
+} |
+ |
+#footnotes div.footnote { |
+ margin: 0 0 5px 0; |
+} |
+ |
+#footnotes hr { |
+ border: none; |
+ border-top: 1px solid silver; |
+ height: 1px; |
+ text-align: left; |
+ margin-left: 0; |
+ width: 20%; |
+ min-width: 100px; |
+} |
+ |
+div.colist td { |
+ padding-right: 0.5em; |
+ padding-bottom: 0.3em; |
+ vertical-align: top; |
+} |
+div.colist td img { |
+ margin-top: 0.3em; |
+} |
+ |
+@media print { |
+ #footer-badges { display: none; } |
+} |
+ |
+#toc { |
+ margin-bottom: 2.5em; |
+} |
+ |
+#toctitle { |
+ color: #527bbd; |
+ font-size: 1.1em; |
+ font-weight: bold; |
+ margin-top: 1.0em; |
+ margin-bottom: 0.1em; |
+} |
+ |
+div.toclevel0, div.toclevel1, div.toclevel2, div.toclevel3, div.toclevel4 { |
+ margin-top: 0; |
+ margin-bottom: 0; |
+} |
+div.toclevel2 { |
+ margin-left: 2em; |
+ font-size: 0.9em; |
+} |
+div.toclevel3 { |
+ margin-left: 4em; |
+ font-size: 0.9em; |
+} |
+div.toclevel4 { |
+ margin-left: 6em; |
+ font-size: 0.9em; |
+} |
+ |
+span.aqua { color: aqua; } |
+span.black { color: black; } |
+span.blue { color: blue; } |
+span.fuchsia { color: fuchsia; } |
+span.gray { color: gray; } |
+span.green { color: green; } |
+span.lime { color: lime; } |
+span.maroon { color: maroon; } |
+span.navy { color: navy; } |
+span.olive { color: olive; } |
+span.purple { color: purple; } |
+span.red { color: red; } |
+span.silver { color: silver; } |
+span.teal { color: teal; } |
+span.white { color: white; } |
+span.yellow { color: yellow; } |
+ |
+span.aqua-background { background: aqua; } |
+span.black-background { background: black; } |
+span.blue-background { background: blue; } |
+span.fuchsia-background { background: fuchsia; } |
+span.gray-background { background: gray; } |
+span.green-background { background: green; } |
+span.lime-background { background: lime; } |
+span.maroon-background { background: maroon; } |
+span.navy-background { background: navy; } |
+span.olive-background { background: olive; } |
+span.purple-background { background: purple; } |
+span.red-background { background: red; } |
+span.silver-background { background: silver; } |
+span.teal-background { background: teal; } |
+span.white-background { background: white; } |
+span.yellow-background { background: yellow; } |
+ |
+span.big { font-size: 2em; } |
+span.small { font-size: 0.6em; } |
+ |
+span.underline { text-decoration: underline; } |
+span.overline { text-decoration: overline; } |
+span.line-through { text-decoration: line-through; } |
+ |
+div.unbreakable { page-break-inside: avoid; } |
+ |
+ |
+/* |
+ * xhtml11 specific |
+ * |
+ * */ |
+ |
+div.tableblock { |
+ margin-top: 1.0em; |
+ margin-bottom: 1.5em; |
+} |
+div.tableblock > table { |
+ border: 3px solid #527bbd; |
+} |
+thead, p.table.header { |
+ font-weight: bold; |
+ color: #527bbd; |
+} |
+p.table { |
+ margin-top: 0; |
+} |
+/* Because the table frame attribute is overriden by CSS in most browsers. */ |
+div.tableblock > table[frame="void"] { |
+ border-style: none; |
+} |
+div.tableblock > table[frame="hsides"] { |
+ border-left-style: none; |
+ border-right-style: none; |
+} |
+div.tableblock > table[frame="vsides"] { |
+ border-top-style: none; |
+ border-bottom-style: none; |
+} |
+ |
+ |
+/* |
+ * html5 specific |
+ * |
+ * */ |
+ |
+table.tableblock { |
+ margin-top: 1.0em; |
+ margin-bottom: 1.5em; |
+} |
+thead, p.tableblock.header { |
+ font-weight: bold; |
+ color: #527bbd; |
+} |
+p.tableblock { |
+ margin-top: 0; |
+} |
+table.tableblock { |
+ border-width: 3px; |
+ border-spacing: 0px; |
+ border-style: solid; |
+ border-color: #527bbd; |
+ border-collapse: collapse; |
+} |
+th.tableblock, td.tableblock { |
+ border-width: 1px; |
+ padding: 4px; |
+ border-style: solid; |
+ border-color: #527bbd; |
+} |
+ |
+table.tableblock.frame-topbot { |
+ border-left-style: hidden; |
+ border-right-style: hidden; |
+} |
+table.tableblock.frame-sides { |
+ border-top-style: hidden; |
+ border-bottom-style: hidden; |
+} |
+table.tableblock.frame-none { |
+ border-style: hidden; |
+} |
+ |
+th.tableblock.halign-left, td.tableblock.halign-left { |
+ text-align: left; |
+} |
+th.tableblock.halign-center, td.tableblock.halign-center { |
+ text-align: center; |
+} |
+th.tableblock.halign-right, td.tableblock.halign-right { |
+ text-align: right; |
+} |
+ |
+th.tableblock.valign-top, td.tableblock.valign-top { |
+ vertical-align: top; |
+} |
+th.tableblock.valign-middle, td.tableblock.valign-middle { |
+ vertical-align: middle; |
+} |
+th.tableblock.valign-bottom, td.tableblock.valign-bottom { |
+ vertical-align: bottom; |
+} |
+ |
+ |
+/* |
+ * manpage specific |
+ * |
+ * */ |
+ |
+body.manpage h1 { |
+ padding-top: 0.5em; |
+ padding-bottom: 0.5em; |
+ border-top: 2px solid silver; |
+ border-bottom: 2px solid silver; |
+} |
+body.manpage h2 { |
+ border-style: none; |
+} |
+body.manpage div.sectionbody { |
+ margin-left: 3em; |
+} |
+ |
+@media print { |
+ body.manpage div#toc { display: none; } |
+} |
+ |
+ |
+</style> |
+<script type="text/javascript"> |
+/*<+'])'); |
+ // Function that scans the DOM tree for header elements (the DOM2 |
+ // nodeIterator API would be a better technique but not supported by all |
+ // browsers). |
+ var iterate = function (el) { |
+ for (var i = el.firstChild; i != null; i = i.nextSibling) { |
+ if (i.nodeType == 1 /* Node.ELEMENT_NODE */) { |
+ var mo = re.exec(i.tagName); |
+ if (mo && (i.getAttribute("class") || i.getAttribute("className")) != "float") { |
+ result[result.length] = new TocEntry(i, getText(i), mo[1]-1); |
+ } |
+ iterate(i); |
+ } |
+ } |
+ } |
+ iterate(el); |
+ return result; |
+ } |
+ |
+ var toc = document.getElementById("toc"); |
+ if (!toc) { |
+ return; |
+ } |
+ |
+ // Delete existing TOC entries in case we're reloading the TOC. |
+ var tocEntriesToRemove = []; |
+ var i; |
+ for (i = 0; i < toc.childNodes.length; i++) { |
+ var entry = toc.childNodes[i]; |
+ if (entry.nodeName.toLowerCase() == 'div' |
+ && entry.getAttribute("class") |
+ && entry.getAttribute("class").match(/^toclevel/)) |
+ tocEntriesToRemove.push(entry); |
+ } |
+ for (i = 0; i < tocEntriesToRemove.length; i++) { |
+ toc.removeChild(tocEntriesToRemove[i]); |
+ } |
+ |
+ // Rebuild TOC entries. |
+ var entries = tocEntries(document.getElementById("content"), toclevels); |
+ for (var i = 0; i < entries.length; ++i) { |
+ var entry = entries[i]; |
+ if (entry.element.id == "") |
+ entry.element.id = "_toc_" + i; |
+ var a = document.createElement("a"); |
+ a.href = "#" + entry.element.id; |
+ a.appendChild(document.createTextNode(entry.text)); |
+ var div = document.createElement("div"); |
+ div.appendChild(a); |
+ div.className = "toclevel" + entry.toclevel; |
+ toc.appendChild(div); |
+ } |
+ if (entries.length == 0) |
+ toc.parentNode.removeChild(toc); |
+}, |
+ |
+ |
+///////////////////////////////////////////////////////////////////// |
+// Footnotes generator |
+///////////////////////////////////////////////////////////////////// |
+ |
+/* Based on footnote generation code from: |
+ * http://www.brandspankingnew.net/archive/2005/07/format_footnote.html |
+ */ |
+ |
+footnotes: function () { |
+ // Delete existing footnote entries in case we're reloading the footnodes. |
+ var i; |
+ var noteholder = document.getElementById("footnotes"); |
+ if (!noteholder) { |
+ return; |
+ } |
+ var entriesToRemove = []; |
+ for (i = 0; i < noteholder.childNodes.length; i++) { |
+ var entry = noteholder.childNodes[i]; |
+ if (entry.nodeName.toLowerCase() == 'div' && entry.getAttribute("class") == "footnote") |
+ entriesToRemove.push(entry); |
+ } |
+ for (i = 0; i < entriesToRemove.length; i++) { |
+ noteholder.removeChild(entriesToRemove[i]); |
+ } |
+ |
+ // Rebuild footnote entries. |
+ var cont = document.getElementById("content"); |
+ var spans = cont.getElementsByTagName("span"); |
+ var refs = {}; |
+ var n = 0; |
+ for (i=0; i<spans.length; i++) { |
+ if (spans[i].className == "footnote") { |
+ n++; |
+ var note = spans[i].getAttribute("data-note"); |
+ if (!note) { |
+ // Use [\s\S] in place of . so multi-line matches work. |
+ // Because JavaScript has no s (dotall) regex flag. |
+ note = spans[i].innerHTML.match(/\s*\[([\s\S]*)]\s*/)[1]; |
+ spans[i].innerHTML = |
+ "[<a id='_footnoteref_" + n + "' href='#_footnote_" + n + |
+ "' title='View footnote' class='footnote'>" + n + "</a>]"; |
+ spans[i].setAttribute("data-note", note); |
+ } |
+ noteholder.innerHTML += |
+ "<div class='footnote' id='_footnote_" + n + "'>" + |
+ "<a href='#_footnoteref_" + n + "' title='Return to text'>" + |
+ n + "</a>. " + note + "</div>"; |
+ var id =spans[i].getAttribute("id"); |
+ if (id != null) refs["#"+id] = n; |
+ } |
+ } |
+ if (n == 0) |
+ noteholder.parentNode.removeChild(noteholder); |
+ else { |
+ // Process footnoterefs. |
+ for (i=0; i<spans.length; i++) { |
+ if (spans[i].className == "footnoteref") { |
+ var href = spans[i].getElementsByTagName("a")[0].getAttribute("href"); |
+ href = href.match(/#.*/)[0]; // Because IE return full URL. |
+ n = refs[href]; |
+ spans[i].innerHTML = |
+ "[<a href='#_footnote_" + n + |
+ "' title='View footnote' class='footnote'>" + n + "</a>]"; |
+ } |
+ } |
+ } |
+}, |
+ |
+install: function(toclevels) { |
+ var timerId; |
+ |
+ function reinstall() { |
+ asciidoc.footnotes(); |
+ if (toclevels) { |
+ asciidoc.toc(toclevels); |
+ } |
+ } |
+ |
+ function reinstallAndRemoveTimer() { |
+ clearInterval(timerId); |
+ reinstall(); |
+ } |
+ |
+ timerId = setInterval(reinstall, 500); |
+ if (document.addEventListener) |
+ document.addEventListener("DOMContentLoaded", reinstallAndRemoveTimer, false); |
+ else |
+ window.onload = reinstallAndRemoveTimer; |
+} |
+ |
+} |
+asciidoc.install(2); |
+/*]]>*/ |
+</script> |
+</head> |
+<body class="article"> |
+<div id="header"> |
+<h1>Dart Cookbook</h1> |
+<span id="author">Shailen Tuli</span><br> |
+<div id="toc"> |
+ <div id="toctitle">Table of Contents</div> |
+ <noscript><p><b>JavaScript must be enabled in your browser to display the table of contents.</b></p></noscript> |
+</div> |
+</div> |
+<div id="content"> |
+<div class="sect1"> |
+<h2 id="_strings">Strings</h2> |
+<div class="sectionbody"> |
+<div class="sect2"> |
+<h3 id="_concatenating_strings">Concatenating Strings</h3> |
+<div class="sect3"> |
+<h4 id="_problem">Problem</h4> |
+<div class="paragraph"><p>You want to concatenate strings in Dart. You tried using <span class="monospaced">+</span>, but that |
+resulted in an error.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution">Solution</h4> |
+<div class="paragraph"><p>Use adjacent string literals:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var fact = 'Dart' 'is' ' fun!'; // 'Dart is fun!'</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion">Discussion</h4> |
+<div class="paragraph"><p>Adjacent literals work over multiple lines:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var fact = 'Dart' |
+'is' |
+'fun!'; // 'Dart is fun!'</pre> |
+</div></div> |
+<div class="paragraph"><p>They also work when using multiline strings:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var lunch = '''Peanut |
+butter''' |
+'''and |
+jelly'''; // 'Peanut\nbutter and\njelly'</pre> |
+</div></div> |
+<div class="paragraph"><p>You can concatenate adjacent single line literals with multiline |
+strings:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var funnyGuys = 'Dewey ' 'Cheatem' |
+''' and |
+Howe'''; // 'Dewey Cheatem and\n Howe'</pre> |
+</div></div> |
+<div class="sect4"> |
+<h5 id="_alternatives_to_adjacent_string_literals">Alternatives to adjacent string literals</h5> |
+<div class="paragraph"><p>You can use the <span class="monospaced">concat()</span> method on a string to concatenate it to |
+another string:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var film = filmToWatch(); |
+film = film.concat('\n'); // 'The Big Lebowski\n'</pre> |
+</div></div> |
+<div class="paragraph"><p>Because <span class="monospaced">concat()</span> creates a new string every time it is invoked, a long |
+chain of <span class="monospaced">concat()</span> s can be expensive. Avoid those. Use a StringBuffer |
+instead (see <em>Incrementally building a string efficiently using a |
+StringBuffer</em>, below).</p></div> |
+<div class="paragraph"><p>Use <span class="monospaced">join()</span> to combine a sequence of strings:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var film = ['The', 'Big', 'Lebowski']).join(' '); // 'The Big Lebowski'</pre> |
+</div></div> |
+<div class="paragraph"><p>You can also use string interpolation to concatenate strings (see |
+<em>Interpolating expressions inside strings</em>, below).</p></div> |
+</div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_interpolating_expressions_inside_strings">Interpolating expressions inside strings</h3> |
+<div class="sect3"> |
+<h4 id="_problem_2">Problem</h4> |
+<div class="paragraph"><p>You want to embed Dart code inside strings.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_2">Solution</h4> |
+<div class="paragraph"><p>You can put the value of an expression inside a string by using |
+${expression}.</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var favFood = 'sushi'; |
+var whatDoILove = 'I love ${favFood.toUpperCase()}'; // 'I love SUSHI'</pre> |
+</div></div> |
+<div class="paragraph"><p>You can skip the {} if the expression is an identifier:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var whatDoILove = 'I love $favFood'; // 'I love sushi'</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_2">Discussion</h4> |
+<div class="paragraph"><p>An interpolated string, <span class="monospaced">'string ${expression}</span> is equivalent to the |
+concatenation of the strings <span class="monospaced">string</span> and <span class="monospaced">expression.toString()</span>. |
+Consider this code:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var four = 4; |
+var seasons = 'The $four seasons'; // 'The 4 seasons'</pre> |
+</div></div> |
+<div class="paragraph"><p>This is functionally equivalent to the following:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var seasons = 'The '.concat(4.toString()).concat(' seasons'); |
+// 'The 4 seasons'</pre> |
+</div></div> |
+<div class="paragraph"><p>You should consider implementing a <span class="monospaced">toString()</span> method for user-defined |
+objects. Here’s what happens if you don’t:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>class Point { |
+ num x, y; |
+ Point(this.x, this.y); |
+} |
+ |
+var point = new Point(3, 4); |
+print('Point: $point'); // "Point: Instance of 'Point'"</pre> |
+</div></div> |
+<div class="paragraph"><p>Probably not what you wanted. Here is the same example with an explicit |
+<span class="monospaced">toString()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>class Point { |
+ ... |
+ |
+ String toString() => 'x: $x, y: $y'; |
+} |
+ |
+print('Point: $point'); // 'Point: x: 3, y: 4'</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_handling_special_characters_within_strings">Handling special characters within strings</h3> |
+<div class="sect3"> |
+<h4 id="_problem_3">Problem</h4> |
+<div class="paragraph"><p>You want to put newlines, dollar signs, or other special characters in strings.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_3">Solution</h4> |
+<div class="paragraph"><p>Prefix special characters with a <span class="monospaced">\</span>.</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre> print(Wile\nCoyote'); |
+ // Wile |
+ // Coyote</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_3">Discussion</h4> |
+<div class="paragraph"><p>Dart designates a few characters as special, and these can be escaped:</p></div> |
+<div class="ulist"><ul> |
+<li> |
+<p> |
+\n for newline, equivalent to \x0A. |
+</p> |
+</li> |
+<li> |
+<p> |
+\r for carriage return, equivalent to \x0D. |
+</p> |
+</li> |
+<li> |
+<p> |
+\f for form feed, equivalent to \x0C. |
+</p> |
+</li> |
+<li> |
+<p> |
+\b for backspace, equivalent to \x08. |
+</p> |
+</li> |
+<li> |
+<p> |
+\t for tab, equivalent to \x09. |
+</p> |
+</li> |
+<li> |
+<p> |
+\v for vertical tab, equivalent to \x0B. |
+</p> |
+</li> |
+</ul></div> |
+<div class="paragraph"><p>If you prefer, you can use <span class="monospaced">\x</span> or <span class="monospaced">\u</span> notation to indicate the special |
+character:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print('Wile\x0ACoyote'); // Same as print('Wile\nCoyote') |
+print('Wile\u000ACoyote'); // Same as print('Wile\nCoyote')</pre> |
+</div></div> |
+<div class="paragraph"><p>You can also use <span class="monospaced">\u{}</span> notation:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print('Wile\u{000A}Coyote'); // same as print('Wile\nCoyote')</pre> |
+</div></div> |
+<div class="paragraph"><p>You can also escape the <span class="monospaced">$</span> used in string interpolation:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var superGenius = 'Wile Coyote'; |
+print('$superGenius and Road Runner'); // 'Wile Coyote and Road Runner' |
+print('\$superGenius and Road Runner'); // '$superGenius and Road Runner'</pre> |
+</div></div> |
+<div class="paragraph"><p>If you escape a non-special character, the <span class="monospaced">\</span> is ignored:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print('Wile \E Coyote'); // 'Wile E Coyote'</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_incrementally_building_a_string_using_a_stringbuffer">Incrementally building a string using a StringBuffer</h3> |
+<div class="sect3"> |
+<h4 id="_problem_4">Problem</h4> |
+<div class="paragraph"><p>You want to collect string fragments and combine them in an efficient |
+manner.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_4">Solution</h4> |
+<div class="paragraph"><p>Use a StringBuffer to programmatically generate a string. Consider this code |
+below for assembling a series of urls from fragments:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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</pre> |
+</div></div> |
+<div class="paragraph"><p>A StringBuffer collects string fragments, but does not generate a new string |
+until <span class="monospaced">toString()</span> is called.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_4">Discussion</h4> |
+<div class="paragraph"><p>Using a StringBuffer is vastly more efficient than concatenating fragments |
+at each step: Consider this rewrite of the above code:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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; |
+}</pre> |
+</div></div> |
+<div class="paragraph"><p>This approach produces the exact same result, but incurs the cost of |
+joining strings multiple times.</p></div> |
+<div class="paragraph"><p>See the <em>Concatenating Strings</em> recipe for a description of <span class="monospaced">concat()</span>.</p></div> |
+<div class="sect4"> |
+<h5 id="_other_stringbuffer_methods">Other StringBuffer methods</h5> |
+<div class="paragraph"><p>In addition to <span class="monospaced">write()</span>, the StringBuffer class provides methods to |
+write a list of strings (<span class="monospaced">writeAll()</span>), write a numerical character code |
+(<span class="monospaced">writeCharCode()</span>), write with an added newline (<span class="monospaced">writeln()</span>), and |
+more. The example below shows how to use these methods:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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!'</pre> |
+</div></div> |
+</div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_determining_whether_a_string_is_empty">Determining whether a string is empty</h3> |
+<div class="sect3"> |
+<h4 id="_problem_5">Problem</h4> |
+<div class="paragraph"><p>You want to know whether a string is empty. You tried <span class="monospaced">if (string) {...}</span>, but |
+that did not work.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_5">Solution</h4> |
+<div class="paragraph"><p>Use <span class="monospaced">string.isEmpty</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var emptyString = ''; |
+print(emptyString.isEmpty); // true</pre> |
+</div></div> |
+<div class="paragraph"><p>You can also just use <span class="monospaced">==</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>if (string == '') {...} // True if string is empty.</pre> |
+</div></div> |
+<div class="paragraph"><p>A string with a space is not empty:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var space = ' '; |
+print(space.isEmpty); // false</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_5">Discussion</h4> |
+<div class="paragraph"><p>Don’t use <span class="monospaced">if (string)</span> to test the emptiness of a string. In Dart, all objects |
+except the boolean true evaluate to false, so <span class="monospaced">if(string)</span> is always false. You |
+will see a warning in the editor if you use an <em>if</em> statement with a non-boolean |
+in checked mode.</p></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_removing_leading_and_trailing_whitespace">Removing leading and trailing whitespace</h3> |
+<div class="sect3"> |
+<h4 id="_problem_6">Problem</h4> |
+<div class="paragraph"><p>You want to remove spaces, tabs, and other whitespace from the beginning and |
+end of strings.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_6">Solution</h4> |
+<div class="paragraph"><p>Use <span class="monospaced">string.trim()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var space = '\n\r\f\t\v'; // A variety of space characters. |
+var string = '$space X $space'; |
+var newString = string.trim(); // 'X'</pre> |
+</div></div> |
+<div class="paragraph"><p>The String class has no methods to remove only leading or only trailing |
+whitespace. You can always use a RegExp.</p></div> |
+<div class="paragraph"><p>Remove only leading whitespace:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var newString = string.replaceFirst(new RegExp(r'^\s+'), ''); // 'X \n\r\f\t\v'</pre> |
+</div></div> |
+<div class="paragraph"><p>Remove only trailing whitespace:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var newString = string.replaceFirst(new RegExp(r'\s+$'), ''); // '\n\r\f\t\v X'</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_changing_string_case">Changing string case</h3> |
+<div class="sect3"> |
+<h4 id="_problem_7">Problem</h4> |
+<div class="paragraph"><p>You want to change the case of strings.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_7">Solution</h4> |
+<div class="paragraph"><p>Use String’s <span class="monospaced">toUpperCase()</span> and <span class="monospaced">toLowerCase()</span> methods:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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é'</pre> |
+</div></div> |
+<div class="paragraph"><p>The <span class="monospaced">toUpperCase()</span> and <span class="monospaced">toLowerCase()</span> methods don’t affect the characters of |
+scripts such as Devanagri that don’t have distinct letter cases.</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var chickenKebab = '\u091a\u093f\u0915\u0928 \u0915\u092c\u093e\u092c'; |
+// 'चिकन कबाब' (in Devanagari) |
+chickenKebab.toLowerCase(); // 'चिकन कबाब' |
+chickenKebab.toUpperCase(); // 'चिकन कबाब'</pre> |
+</div></div> |
+<div class="paragraph"><p>If a character’s case does not change when using <span class="monospaced">toUpperCase()</span> and |
+<span class="monospaced">toLowerCase()</span>, it is most likely because the character only has one |
+form.</p></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_handling_extended_characters_that_are_composed_of_multiple_code_units">Handling extended characters that are composed of multiple code units</h3> |
+<div class="sect3"> |
+<h4 id="_problem_8">Problem</h4> |
+<div class="paragraph"><p>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?</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_8">Solution</h4> |
+<div class="paragraph"><p>You can create an extended character using <span class="monospaced">'\u{}'</span> syntax:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var clef = '\u{1F3BC}'; // 🎼</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_6">Discussion</h4> |
+<div class="paragraph"><p>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 <em>surrogate pair</em> is often used to |
+describe these strings.</p></div> |
+<div class="paragraph"><p>The clef glyph <span class="monospaced">'\u{1F3BC}'</span> is composed of the <span class="monospaced">'\uD83C'</span> and <span class="monospaced">'\uDFBC'</span> |
+surrogate pair.</p></div> |
+<div class="paragraph"><p>You can get an extended string’s surrogate pair through its <span class="monospaced">codeUnits</span> |
+property:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>clef.codeUnits.map((codeUnit) => codeUnit.toRadixString(16)); |
+// ['d83c', 'dfbc']</pre> |
+</div></div> |
+<div class="paragraph"><p>Accessing a surrogate pair member leads to errors, and you should avoid |
+properties and methods that expose it:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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.</pre> |
+</div></div> |
+<div class="paragraph"><p>When dealing with strings containing extended characters, you should use the |
+<span class="monospaced">runes</span> getter.</p></div> |
+<div class="paragraph"><p>To get the string’s length, use <span class="monospaced">string.runes.length</span>. Don’t use |
+<span class="monospaced">string.length</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print(clef.runes.length); // 1 |
+print(clef.length); // 2 |
+print(clef.codeUnits.length); // 2</pre> |
+</div></div> |
+<div class="paragraph"><p>To get an individual character or its numeric equivalent, index the rune list:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print(clef.runes.toList()[0]); // 127932 ('\u{1F3BC}')</pre> |
+</div></div> |
+<div class="paragraph"><p>To get the string’s characters as a list, map the string runes:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var clef = '\u{1F3BC}'; // 🎼 |
+var title = '$clef list:' |
+print(subject.runes.map((rune) => new String.fromCharCode(rune)).toList()); |
+// ['🎼', ' ', 'l', 'i', 's', 't', ':']</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_converting_between_characters_and_numerical_codes">Converting between characters and numerical codes</h3> |
+<div class="sect3"> |
+<h4 id="_problem_9">Problem</h4> |
+<div class="paragraph"><p>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.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_9">Solution</h4> |
+<div class="paragraph"><p>Use the <span class="monospaced">runes</span> getter to get a string’s code points:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>'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}']).</pre> |
+</div></div> |
+<div class="paragraph"><p>Use <span class="monospaced">string.codeUnits</span> to get a string’s UTF-16 code units:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>'Dart'.codeUnits.toList(); // [68, 97, 114, 116] |
+smileyFace.codeUnits.toList(); // [9786] |
+clef.codeUnits.toList(); // [55356, 57276]</pre> |
+</div></div> |
+<div class="sect4"> |
+<h5 id="_using_codeunitat_to_get_individual_code_units">Using codeUnitAt() to get individual code units</h5> |
+<div class="paragraph"><p>To get the code unit at a particular index, use <span class="monospaced">codeUnitAt()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>'Dart'.codeUnitAt(0); // 68 |
+smileyFace.codeUnitAt(0); // 9786 (the decimal value of '\u263A') |
+clef.codeUnitAt(0); // 55356 (does not represent a legal string)</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_converting_numerical_codes_to_strings">Converting numerical codes to strings</h4> |
+<div class="paragraph"><p>You can generate a new string from numerical codes using the factory |
+<span class="monospaced">String.fromCharCodes(charCodes)</span>. You can pass either runes or code units and |
+<span class="monospaced">String.fromCharCodes(charCodes)</span> can tell the difference and do the right |
+thing automatically:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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])); // 🎼</pre> |
+</div></div> |
+<div class="paragraph"><p>You can use the <span class="monospaced">String.fromCharCode()</span> factory to convert a single rune |
+or code unit to a string:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>new String.fromCharCode(68); // 'D' |
+new String.fromCharCode(9786); // ☺ |
+new String.fromCharCode(127932); // 🎼</pre> |
+</div></div> |
+<div class="paragraph"><p>Creating a string with only one half of a surrogate pair is permitted, |
+but not recommended.</p></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_calculating_the_length_of_a_string">Calculating the length of a string</h3> |
+<div class="sect3"> |
+<h4 id="_problem_10">Problem</h4> |
+<div class="paragraph"><p>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.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_10">Solution</h4> |
+<div class="paragraph"><p>Use <span class="monospaced">string.runes.length</span> to get the number of characters in a string.</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print('I love music'.runes.length); // 12</pre> |
+</div></div> |
+<div class="paragraph"><p>You can safely use <span class="monospaced">string.runes.length</span> to get the length of strings that |
+contain extended characters:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var clef = '\u{1F3BC}'; // 🎼 |
+var subject = '$clef list:'; // |
+var music = 'I $hearts $clef'; // 'I ♡ 🎼 ' |
+ |
+clef.runes.length; // 1 |
+music.runes.length // 5</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_7">Discussion</h4> |
+<div class="paragraph"><p>You can directly use a string’s <span class="monospaced">length</span> property (minus <span class="monospaced">runes</span>). This returns |
+the string’s code unit length. Using <span class="monospaced">string.length</span> produces the same length |
+as <span class="monospaced">string.runes.length</span> for most unicode characters.</p></div> |
+<div class="paragraph"><p>For extended characters, the code unit length is one more than the rune |
+length:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>clef.length; // 2 |
+ |
+var music = 'I $hearts $clef'; // 'I ♡ 🎼 ' |
+music.length; // 6</pre> |
+</div></div> |
+<div class="paragraph"><p>Unless you specifically need the code unit length of a string, use |
+<span class="monospaced">string.runes.length</span>.</p></div> |
+<div class="sect4"> |
+<h5 id="_working_with_combined_characters">Working with combined characters</h5> |
+<div class="paragraph"><p>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 <em>Dart</em> has four characters, and <em>Amelié</em> |
+has six, right? Almost. The length of <em>Dart</em> is indeed four, but the length of |
+<em>Amelié</em> depends on how that string was constructed:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var name = 'Ameli\u00E9'; // 'Amelié' |
+var anotherName = 'Ameli\u0065\u0301'; // 'Amelié' |
+print(name.length); // 6 |
+print(anotherName.length); // 7</pre> |
+</div></div> |
+<div class="paragraph"><p>Both <span class="monospaced">name</span> and <span class="monospaced">anotherName</span> return strings that look the same, but where |
+the <em>é</em> is constructed using a different number of runes. This makes it |
+impossible to know the length of these strings by just looking at them.</p></div> |
+</div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_processing_a_string_one_character_at_a_time">Processing a string one character at a time</h3> |
+<div class="sect3"> |
+<h4 id="_problem_11">Problem</h4> |
+<div class="paragraph"><p>You want to do something with each character in a string.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_11">Solution</h4> |
+<div class="paragraph"><p>Map the results of calling <span class="monospaced">string.split('')</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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', ' ', '☺']</pre> |
+</div></div> |
+<div class="paragraph"><p>Or, loop over the characters of a string:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var list = []; |
+for(var i = 0; i < lang.length; i++) { |
+ list.add('*${lang[i]}*'); |
+} |
+ |
+print(list); // ['*D*', '*a*', '*r*', '*t*']</pre> |
+</div></div> |
+<div class="paragraph"><p>Or, map the string runes:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>// ['*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(); |
+});</pre> |
+</div></div> |
+<div class="paragraph"><p>When working with extended characters, you should always map the string runes. |
+Don’t use <span class="monospaced">split('')</span> and avoid indexing an extended string. See the <em>Handling |
+extended characters that are composed of multiple code units</em> recipe for |
+special considerations when working with extended strings.</p></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_splitting_a_string_into_substrings">Splitting a string into substrings</h3> |
+<div class="sect3"> |
+<h4 id="_problem_12">Problem</h4> |
+<div class="paragraph"><p>You want to split a string into substrings using a delimiter or a pattern.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_12">Solution</h4> |
+<div class="paragraph"><p>Use the <span class="monospaced">split()</span> method with a string or a RegExp as an argument.</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var smileyFace = '\u263A'; |
+var happy = 'I am $smileyFace'; |
+happy.split(' '); // ['I', 'am', '☺']</pre> |
+</div></div> |
+<div class="paragraph"><p>Here is an example of using <span class="monospaced">split()</span> with a RegExp:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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']</pre> |
+</div></div> |
+<div class="paragraph"><p>In the code above, the string <span class="monospaced">nums</span> contains various numbers, some of which |
+are expressed as fractions or as int-divisions. A RegExp splits the string to |
+extract just the numbers.</p></div> |
+<div class="paragraph"><p>You can perform operations on the matched and unmatched portions of a string |
+when using <span class="monospaced">split()</span> with a RegExp:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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'</pre> |
+</div></div> |
+<div class="paragraph"><p>The RegExp matches the middle word (<em>SHOOTS</em>). A pair of callbacks are |
+registered to transform the matched and unmatched substrings before the |
+substrings are joined together again.</p></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_determining_whether_a_string_contains_another_string">Determining whether a string contains another string</h3> |
+<div class="sect3"> |
+<h4 id="_problem_13">Problem</h4> |
+<div class="paragraph"><p>You want to find out whether a string is the substring of another string.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_13">Solution</h4> |
+<div class="paragraph"><p>Use <span class="monospaced">string.contains()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var fact = 'Dart strings are immutable'; |
+print(fact.contains('immutable')); // True.</pre> |
+</div></div> |
+<div class="paragraph"><p>You can use a second argument to specify where in the string to start looking:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print(fact.contains('Dart', 2)); // False</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_8">Discussion</h4> |
+<div class="paragraph"><p>The String class provides a couple of shortcuts for testing whether a |
+string is a substring of another:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print(string.startsWith('Dart')); // True. |
+print(string.endsWith('e')); // True.</pre> |
+</div></div> |
+<div class="paragraph"><p>You can also use <span class="monospaced">string.indexOf()</span>, which returns -1 if the substring |
+is not found within a string, and otherwise returns the matching index:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var found = string.indexOf('art') != -1; // True, `art` is found in `Dart`.</pre> |
+</div></div> |
+<div class="paragraph"><p>You can also use a RegExp and <span class="monospaced">hasMatch()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var found = new RegExp(r'ar[et]').hasMatch(string); |
+// True, 'art' and 'are' match.</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_finding_matches_of_a_regexp_pattern_in_a_string">Finding matches of a RegExp pattern in a string</h3> |
+<div class="sect3"> |
+<h4 id="_problem_14">Problem</h4> |
+<div class="paragraph"><p>You want to use RegExp to match a pattern in a string, and want to be |
+able to access the matches.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_14">Solution</h4> |
+<div class="paragraph"><p>Construct a regular expression using the RegExp class, and find matches |
+using the <span class="monospaced">allMatches()</span> method:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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']</pre> |
+</div></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_discussion_9">Discussion</h4> |
+<div class="paragraph"><p>You can query the object returned by <span class="monospaced">allMatches()</span> to find out the |
+number of matches:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var howManyMatches = matches.length; // 2</pre> |
+</div></div> |
+<div class="paragraph"><p>To find the first match, use <span class="monospaced">firstMatch()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var firstMatch = RegExp.firstMatch(neverEatingThat).group(0); // 'fox'</pre> |
+</div></div> |
+<div class="paragraph"><p>To directly get the matched string, use <span class="monospaced">stringMatch()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>print(regExp.stringMatch(neverEatingThat)); // 'fox' |
+print(regExp.stringMatch('I like bagels and lox')); // null</pre> |
+</div></div> |
+</div> |
+</div> |
+<div class="sect2"> |
+<h3 id="_substituting_strings_based_on_regexp_matches">Substituting strings based on RegExp matches</h3> |
+<div class="sect3"> |
+<h4 id="_problem_15">Problem</h4> |
+<div class="paragraph"><p>You want to match substrings within a string and make substitutions |
+based on the matches.</p></div> |
+</div> |
+<div class="sect3"> |
+<h4 id="_solution_15">Solution</h4> |
+<div class="paragraph"><p>Construct a regular expression using the RegExp class and make |
+replacements using <span class="monospaced">replaceAll()</span> method:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>var resume = 'resume'.replaceAll(new RegExp(r'e'), '\u00E9'); // 'résumé'</pre> |
+</div></div> |
+<div class="paragraph"><p>If you want to replace just the first match, use <span class="monospaced">replaceFirst()</span>:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>// Replace the first match of one or more zeros with an empty string. |
+var smallNum = '0.0001'.replaceFirst(new RegExp(r'0+'), ''); // '.0001'</pre> |
+</div></div> |
+<div class="paragraph"><p>You can use <span class="monospaced">replaceAllMatched()</span> to register a function that modifies the |
+matches:</p></div> |
+<div class="listingblock"> |
+<div class="content monospaced"> |
+<pre>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'</pre> |
+</div></div> |
+</div> |
+</div> |
+</div> |
+</div> |
+</div> |
+<div id="footnotes"><hr></div> |
+<div id="footer"> |
+<div id="footer-text"> |
+Last updated 2013-03-16 10:56:58 PDT |
+</div> |
+</div> |
+</body> |
+</html> |