<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Io on Roberto Selbach</title><link>https://rselbach.com/tags/io/</link><description>Recent content in Io on Roberto Selbach</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Tue, 16 Jul 2013 17:33:00 +0000</lastBuildDate><atom:link href="https://rselbach.com/tags/io/index.xml" rel="self" type="application/rss+xml"/><item><title>Reading lines with bufio.Scanner</title><link>https://rselbach.com/reading-lines-with-bufio-scanner/</link><pubDate>Tue, 16 Jul 2013 17:33:00 +0000</pubDate><guid>https://rselbach.com/reading-lines-with-bufio-scanner/</guid><description>&lt;p&gt;Every small text-processing program I write begins with the same lie: “I only need to read a file line by line.” Five minutes later I am handling buffering, end-of-file, and the final line without a newline.&lt;/p&gt;
&lt;p&gt;Go 1.1 added &lt;code&gt;bufio.Scanner&lt;/code&gt;, which packages that loop neatly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;scanner&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;bufio&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;NewScanner&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;r&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scanner&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Scan&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;line&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scanner&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Text&lt;/span&gt;()
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#a6e22e"&gt;fmt&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Println&lt;/span&gt;(&lt;span style="color:#a6e22e"&gt;line&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;:=&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;scanner&lt;/span&gt;.&lt;span style="color:#a6e22e"&gt;Err&lt;/span&gt;(); &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt; &lt;span style="color:#f92672"&gt;!=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;nil&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;	&lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;err&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;By default Scanner splits input into lines and removes the line ending. &lt;code&gt;Scan&lt;/code&gt; advances to the next token. &lt;code&gt;Text&lt;/code&gt; returns that token as a string; &lt;code&gt;Bytes&lt;/code&gt; returns the token as bytes. After the loop, &lt;code&gt;Err&lt;/code&gt; distinguishes a clean end-of-input from a read or tokenization error.&lt;/p&gt;
&lt;p&gt;That final check is easy to omit because the loop looks complete without it. It is not. An I/O error halfway through a configuration file should not quietly turn the remaining configuration into an unusually convincing empty section.&lt;/p&gt;
&lt;p&gt;Scanner is more general than lines. &lt;code&gt;Split&lt;/code&gt; accepts a split function, and the package supplies functions for words, bytes, and runes. A split function receives available data and whether the underlying reader has reached its end. It reports how far to advance, which token to return, and any error. This supports simple lexical work without writing the buffering loop again.&lt;/p&gt;
&lt;p&gt;There is an important limit: Scanner uses a maximum token size. It is intended for reasonably sized tokens, not arbitrary enormous records. A log format that permits a single multi-megabyte line may exceed the limit and stop scanning with an error. For such input, &lt;code&gt;bufio.Reader&lt;/code&gt; and an explicit loop provide more control. Choosing Scanner means accepting its token-oriented contract, not merely admiring its shorter spelling.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Bytes&lt;/code&gt; also deserves care. The returned slice may refer to storage Scanner reuses on the next call to &lt;code&gt;Scan&lt;/code&gt;. Process it before advancing, or copy it if it must survive. &lt;code&gt;Text&lt;/code&gt; gives a string representation that is safer to retain, with the corresponding conversion and allocation considerations.&lt;/p&gt;
&lt;p&gt;I also avoid mixing direct reads from the underlying reader with Scanner. Scanner buffers ahead, so the reader&amp;rsquo;s apparent position need not correspond to the end of the last token I processed. One owner for the stream is a much less surprising arrangement.&lt;/p&gt;
&lt;p&gt;I replaced my hand-written line reader with Scanner and deleted more edge-case code than I added. That is a good trade when its token limits fit the data. When they do not, the lower-level reader remains available. Go&amp;rsquo;s I/O packages are at their best when the simple case is genuinely simple and the escape hatch is still visible.&lt;/p&gt;</description></item></channel></rss>