mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2025-12-19 04:04:20 +08:00
rebuild
This commit is contained in:
172
ch3/ch3-05.html
172
ch3/ch3-05.html
@@ -21,6 +21,10 @@
|
||||
<link rel="stylesheet" href="../gitbook/style.css">
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-katex/katex.min.css">
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../gitbook/plugins/gitbook-plugin-highlight/website.css">
|
||||
|
||||
|
||||
@@ -44,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Fri Dec 25 2015 12:32:44 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="3.5" data-chapter-title="字符串" data-filepath="ch3/ch3-05.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -238,7 +242,7 @@
|
||||
|
||||
<b>1.5.</b>
|
||||
|
||||
穫取URL
|
||||
獲取URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -253,7 +257,7 @@
|
||||
|
||||
<b>1.6.</b>
|
||||
|
||||
併發穫取多個URL
|
||||
併發獲取多個URL
|
||||
</a>
|
||||
|
||||
|
||||
@@ -802,7 +806,7 @@
|
||||
|
||||
<b>5.10.</b>
|
||||
|
||||
Recover捕穫異常
|
||||
Recover捕獲異常
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1315,7 +1319,7 @@
|
||||
|
||||
<b>8.9.</b>
|
||||
|
||||
併發的退齣
|
||||
併發的退出
|
||||
</a>
|
||||
|
||||
|
||||
@@ -1834,7 +1838,7 @@
|
||||
|
||||
<b>12.7.</b>
|
||||
|
||||
穫取結構體字段標識
|
||||
獲取結構體字段標識
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2020,48 +2024,48 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="35-字符串">3.5. 字符串</h2>
|
||||
<p>一個字符串是一個不可改變的字節序列. 字符串可以包含任意的數據, 包括字節值0, 但是通常包含人類可讀的文本. 文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點(rune)序列, 我們稍後會詳細討論這個問題.</p>
|
||||
<p>內置的 len 函數可以返迴一個字符串的字節數目(不是rune字符數目), 索引操作 s[i] 返迴第i個字節的字節值, i 必鬚滿足 0 ≤ i< len(s) 條件約束.</p>
|
||||
<p>一個字符串是一個不可改變的字節序列。字符串可以包含任意的數據,包括byte值0,但是通常是用來包含人類可讀的文本。文本字符串通常被解釋爲采用UTF8編碼的Unicode碼點(rune)序列,我們稍後會詳細討論這個問題。</p>
|
||||
<p>內置的len函數可以返迴一個字符串中的字節數目(不是rune字符數目),索引操作s[i]返迴第i個字節的字節值,i必鬚滿足0 ≤ i< len(s)條件約束。</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"hello, world"</span>
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "12"</span>
|
||||
fmt.Println(s[<span class="hljs-number">0</span>], s[<span class="hljs-number">7</span>]) <span class="hljs-comment">// "104 119" ('h' and 'w')</span>
|
||||
</code></pre>
|
||||
<p>如果視圖訪問超齣字符串范圍的字節將會導致panic異常:</p>
|
||||
<p>如果試圖訪問超出字符串索引范圍的字節將會導致panic異常:</p>
|
||||
<pre><code class="lang-Go">c := s[<span class="hljs-built_in">len</span>(s)] <span class="hljs-comment">// panic: index out of range</span>
|
||||
</code></pre>
|
||||
<p>第i個字節併不一定是字符串的第i個字符, 因此對於非ASCII字符的UTF8編碼會要兩個或多個字節. 我們簡單説下字符的工作方式.</p>
|
||||
<p>子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節(併不包含j本身)生成一個新字符串. 生成的子字符串將包含 j-i 個字節.</p>
|
||||
<p>第i個字節併不一定是字符串的第i個字符,因爲對於非ASCII字符的UTF8編碼會要兩個或多個字節。我們先簡單説下字符的工作方式。</p>
|
||||
<p>子字符串操作s[i:j]基於原始的s字符串的第i個字節開始到第j個字節(併不包含j本身)生成一個新字符串。生成的新字符串將包含j-i個字節。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s[<span class="hljs-number">0</span>:<span class="hljs-number">5</span>]) <span class="hljs-comment">// "hello"</span>
|
||||
</code></pre>
|
||||
<p>同樣, 如果索引超齣字符串范圍或者j小於i的話將導致panic異常.</p>
|
||||
<p>不管i還是j都可能被忽略, 當它們被忽略時將采用0作爲開始位置, 采用 len(s) 作爲接受的位置.</p>
|
||||
<p>同樣,如果索引超出字符串范圍或者j小於i的話將導致panic異常。</p>
|
||||
<p>不管i還是j都可能被忽略,當它們被忽略時將采用0作爲開始位置,采用len(s)作爲結束的位置。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s[:<span class="hljs-number">5</span>]) <span class="hljs-comment">// "hello"</span>
|
||||
fmt.Println(s[<span class="hljs-number">7</span>:]) <span class="hljs-comment">// "world"</span>
|
||||
fmt.Println(s[:]) <span class="hljs-comment">// "hello, world"</span>
|
||||
</code></pre>
|
||||
<p>其中 + 操作符將兩個字符串鏈接構造一個新字符串:</p>
|
||||
<p>其中+操作符將兩個字符串鏈接構造一個新字符串:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-string">"goodbye"</span> + s[<span class="hljs-number">5</span>:]) <span class="hljs-comment">// "goodbye, world"</span>
|
||||
</code></pre>
|
||||
<p>字符串可以用 == 和 < 進行比較; 比較通過逐個字節比較完成的, 因此比較的結果是字符串自然編碼的順序.</p>
|
||||
<p>字符串的值是不可變的: 一個字符串包含的字節序列永遠不會被改變, 當然我們也可以給一個字符串變量分配一個新字符串值. 可以像下面這樣將一個字符串追加到另一個字符串</p>
|
||||
<p>字符串可以用==和<進行比較;比較通過逐個字節比較完成的,因此比較的結果是字符串自然編碼的順序。</p>
|
||||
<p>字符串的值是不可變的:一個字符串包含的字節序列永遠不會被改變,當然我們也可以給一個字符串變量分配一個新字符串值。可以像下面這樣將一個字符串追加到另一個字符串:</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"left foot"</span>
|
||||
t := s
|
||||
s += <span class="hljs-string">", right foot"</span>
|
||||
</code></pre>
|
||||
<p>這併不會導致原始的字符串值被改變, 但是 s 將因爲 += 語句持有一個新的字符串值, 但是 t 依然是包含原先的字符串值.</p>
|
||||
<p>這併不會導致原始的字符串值被改變,但是變量s將因爲+=語句持有一個新的字符串值,但是t依然是包含原先的字符串值。</p>
|
||||
<pre><code class="lang-Go">fmt.Println(s) <span class="hljs-comment">// "left foot, right foot"</span>
|
||||
fmt.Println(t) <span class="hljs-comment">// "left foot"</span>
|
||||
</code></pre>
|
||||
<p>因爲字符串是不可脩改的, 因此嚐試脩改字符串內部數據的操作是被禁止的:</p>
|
||||
<p>因爲字符串是不可脩改的,因此嚐試脩改字符串內部數據的操作也是被禁止的:</p>
|
||||
<pre><code class="lang-Go">s[<span class="hljs-number">0</span>] = <span class="hljs-string">'L'</span> <span class="hljs-comment">// compile error: cannot assign to s[0]</span>
|
||||
</code></pre>
|
||||
<p>不變性意味如果兩個字符串共享相同的底層數據是安全的, 這使得複製任何長度的字符串代價是低廉的. 同樣, 一個字符串 s 和對應的子字符串 s[7:] 也可以安全地共享相同的內存, 因此字符串切片操作代價也是低廉的. 在這兩種情況下都沒有必要分配新的內存. 圖3.4 演示了一個字符串和兩個字串共享相同的底層數據.</p>
|
||||
<p>不變性意味如果兩個字符串共享相同的底層數據的話也是安全的,這使得複製任何長度的字符串代價是低廉的。同樣,一個字符串s和對應的子字符串切片s[7:]的操作也可以安全地共享相同的內存,因此字符串切片操作代價也是低廉的。在這兩種情況下都沒有必要分配新的內存。 圖3.4演示了一個字符串和兩個字串共享相同的底層數據。</p>
|
||||
<h3 id="351-字符串面值">3.5.1. 字符串面值</h3>
|
||||
<p>字符串值也可以用字符串面值方式編寫, 隻要將一繫列字節序列包含在雙引號卽可:</p>
|
||||
<p>字符串值也可以用字符串面值方式編寫,隻要將一繫列字節序列包含在雙引號卽可:</p>
|
||||
<pre><code>"Hello, 世界"
|
||||
</code></pre><p><img src="../images/ch3-04.png" alt=""></p>
|
||||
<p>因爲Go語言源文件總是用UTF8編碼, 併且Go的文本字符串也以UTF8編碼的方式處理, 我們可以將Unicode碼點也寫到字符串面值中.</p>
|
||||
<p>在一個雙引號包含的字符串面值中, 可以用以反斜槓\開頭的轉義序列插入任意的數據. 下面換行, 迴車和 製表符等常見的ASCII控製代碼的轉義方式:</p>
|
||||
<p>因爲Go語言源文件總是用UTF8編碼,併且Go語言的文本字符串也以UTF8編碼的方式處理,因此我們可以將Unicode碼點也寫到字符串面值中。</p>
|
||||
<p>在一個雙引號包含的字符串面值中,可以用以反斜槓<code>\</code>開頭的轉義序列插入任意的數據。下面的換行、迴車和製表符等是常見的ASCII控製代碼的轉義方式:</p>
|
||||
<pre><code>\a 響鈴
|
||||
\b 退格
|
||||
\f 換頁
|
||||
@@ -2072,9 +2076,9 @@ fmt.Println(t) <span class="hljs-comment">// "left foot"</span>
|
||||
\' 單引號 (隻用在 '\'' 形式的rune符號面值中)
|
||||
\" 雙引號 (隻用在 "..." 形式的字符串面值中)
|
||||
\\ 反斜槓
|
||||
</code></pre><p>可以通過十六進製或八進製轉義在字符串面值包含任意的字節. 一個十六進製的轉義是 \xhh, 其中兩個h表示十六進製數字(大寫或小寫都可以). 一個八進製轉義是 \ooo, 包含三個八進製的o數字(0到7), 但是不能超過\377. 每一個單一的字節表達一個特定的值. 稍後我們將看到如何將一個Unicode碼點寫到字符串面值中.</p>
|
||||
<p>一個原生的字符串面值形式是 <code>...</code>, 使用反引號 ``` 代替雙引號. 在原生的字符串面值中, 沒有轉義操作; 全部的內容都是字面的意思, 包含退格和換行, 因此一個程序中的原生字符串面值可能跨越多行. 唯一的特殊處理是是刪除迴車以保證在所有平颱上的值都是一樣的, 包括那些把迴車也放入文本文件的繫統.</p>
|
||||
<p>原生字符串面值用於編寫正則表達式會很方便, 因爲正則表達式往往會包含很多反斜槓. 原生字符串面值同時廣泛應用於HTML模闆, JSON面值, 命令行提示信息, 以及那些需要擴展到多行的場景.</p>
|
||||
</code></pre><p>可以通過十六進製或八進製轉義在字符串面值包含任意的字節。一個十六進製的轉義形式是\xhh,其中兩個h表示十六進製數字(大寫或小寫都可以)。一個八進製轉義形式是\ooo,包含三個八進製的o數字(0到7),但是不能超過<code>\377</code>(譯註:對應一個字節的范圍,十進製爲255)。每一個單一的字節表達一個特定的值。稍後我們將看到如何將一個Unicode碼點寫到字符串面值中。</p>
|
||||
<p>一個原生的字符串面值形式是<code>...</code>,使用反引號<code>代替雙引號。在原生的字符串面值中,沒有轉義操作;全部的內容都是字面的意思,包含退格和換行,因此一個程序中的原生字符串面值可能跨越多行(譯註:在原生字符串面值內部是無法直接寫</code>字符的,可以用八進製或十六進製轉義或+"```"鏈接字符串常量完成)。唯一的特殊處理是會刪除迴車以保證在所有平台上的值都是一樣的,包括那些把迴車也放入文本文件的繫統(譯註:Windows繫統會把迴車和換行一起放入文本文件中)。</p>
|
||||
<p>原生字符串面值用於編寫正則表達式會很方便,因爲正則表達式往往會包含很多反斜槓。原生字符串面值同時被廣泛應用於HTML模闆、JSON面值、命令行提示信息以及那些需要擴展到多行的場景。</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">const</span> GoUsage = <span class="hljs-string">`Go is a tool for managing Go source code.
|
||||
|
||||
Usage:
|
||||
@@ -2082,38 +2086,38 @@ Usage:
|
||||
...`</span>
|
||||
</code></pre>
|
||||
<h3 id="352-unicode">3.5.2. Unicode</h3>
|
||||
<p>在很久以前, 世界比較簡單的, 起碼計算機就隻有一個ASCII字符集: 美國信息交換標準代碼. ASCII, 更準確地説是美國的ASCII, 使用 7bit 來表示 128 個字符: 包含英文字母的大小寫, 數字, 各種標點符號和設置控製符. 對於早期的計算機程序, 這些足夠了, 但是這也導致了世界上很多其他地區的用戶無法直接使用自己的書寫繫統. 隨着互聯網的發展, 混合多種語言的數據變了很常見. 如何有效處理這些包含了各種語言的豐富多樣的數據呢?</p>
|
||||
<p>答案就是使用Unicode(unicode.org), 它收集了這個世界上所有的書寫繫統, 包括重音符號和其他變音符號, 製表符和迴車符, 還有很多神祕符號, 每個符號都分配一個Unicode碼點, Unicode碼點對應Go語言中的rune類型.</p>
|
||||
<p>第八版本的Unicode標準收集了超過120,000個字符, 涵蓋超過100種語言. 這些在計算機程序和數據中是如何體現的那? 通用的表示一個Unicode碼點的數據類型是int32, 也就是Go語言中rune對應的類型; 它的同義詞rune符文正是這個意思.</p>
|
||||
<p>我們可以將一個符文序列表示爲一個int32序列. 這種編碼方式叫UTF-32或UCS-4, 每個Unicode碼點都使用同樣的大小32bit來表示. 這種方式比較簡單統一, 它會浪費很多存儲空間, 因爲大數據計算機可讀的文本是ASCII字符, 本來每個ASCII字符隻需要8bit或1字節就能表示. 卽使是常用的字符也遠少於65,536個, 也就是説用16bit編碼方式就能表達常用字符. 但是, 還有更好的編碼方法嗎?</p>
|
||||
<p>在很久以前,世界還是比較簡單的,起碼計算機世界就隻有一個ASCII字符集:美国信息交換標準代碼。ASCII,更準確地説是美国的ASCII,使用7bit來表示128個字符:包含英文字母的大小寫、數字、各種標點符號和設置控製符。對於早期的計算機程序來説,這些就足夠了,但是這也導致了世界上很多其他地區的用戶無法直接使用自己的符號繫統。隨着互聯網的發展,混合多種語言的數據變得很常見(譯註:比如本身的英文原文或中文翻譯都包含了ASCII、中文、日文等多種語言字符)。如何有效處理這些包含了各種語言的豐富多樣的文本數據呢?</p>
|
||||
<p>答案就是使用Unicode( <a href="http://unicode.org" target="_blank">http://unicode.org</a> ),它收集了這個世界上所有的符號繫統,包括重音符號和其它變音符號,製表符和迴車符,還有很多神祕的符號,每個符號都分配一個唯一的Unicode碼點,Unicode碼點對應Go語言中的rune整數類型(譯註:rune是int32等價類型)。</p>
|
||||
<p>在第八版本的Unicode標準收集了超過120,000個字符,涵蓋超過100多種語言。這些在計算機程序和數據中是如何體現的呢?通用的表示一個Unicode碼點的數據類型是int32,也就是Go語言中rune對應的類型;它的同義詞rune符文正是這個意思。</p>
|
||||
<p>我們可以將一個符文序列表示爲一個int32序列。這種編碼方式叫UTF-32或UCS-4,每個Unicode碼點都使用同樣的大小32bit來表示。這種方式比較簡單統一,但是它會浪費很多存儲空間,因爲大數據計算機可讀的文本是ASCII字符,本來每個ASCII字符隻需要8bit或1字節就能表示。而且卽使是常用的字符也遠少於65,536個,也就是説用16bit編碼方式就能表達常用字符。但是,還有其它更好的編碼方法嗎?</p>
|
||||
<h3 id="353-utf8">3.5.3. UTF-8</h3>
|
||||
<p>UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼. UTF8編碼由Go語言之父 Ken Thompson 和 Rob Pike 共同發明, 現在已經是Unicode的標準. UTF8使用1到4個字節來表示每個Unicode碼點符號, ASCII部分字符隻使用1個字節, 常用字符部分使用2或3個字節. 每個符號編碼後第一個字節的高端bit位用於表示總共有多少個字節. 如果第一個字節的高端bit爲0, 則表示對應7bit的ASCII字符, 每個字符一個字節, 和傳統的ASCII編碼兼容. 如果第一個字節的高端bit是110, 則説明需要2個字節; 後續的每個高端bit都以10開頭. 更大的Unicode碼點也是采用類似的策略處理.</p>
|
||||
<p>UTF8是一個將Unicode碼點編碼爲字節序列的變長編碼。UTF8編碼由Go語言之父Ken Thompson和Rob Pike共同發明的,現在已經是Unicode的標準。UTF8編碼使用1到4個字節來表示每個Unicode碼點,ASCII部分字符隻使用1個字節,常用字符部分使用2或3個字節表示。每個符號編碼後第一個字節的高端bit位用於表示總共有多少編碼個字節。如果第一個字節的高端bit爲0,則表示對應7bit的ASCII字符,ASCII字符每個字符依然是一個字節,和傳統的ASCII編碼兼容。如果第一個字節的高端bit是110,則説明需要2個字節;後續的每個高端bit都以10開頭。更大的Unicode碼點也是采用類似的策略處理。</p>
|
||||
<pre><code>0xxxxxxx runes 0-127 (ASCII)
|
||||
110xxxxx 10xxxxxx 128-2047 (values <128 unused)
|
||||
1110xxxx 10xxxxxx 10xxxxxx 2048-65535 (values <2048 unused)
|
||||
11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 65536-0x10ffff (other values unused)
|
||||
</code></pre><p>變長的編碼無法直接通過索引來訪問第n個字符, 但是UTF8穫得了很多額外的優點. 首先UTF8編碼比較緊湊, 兼容ASCII, 併且可以自動同步: 它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置. 它也是一個前綴編碼, 所以當從左向右解碼時不會有任何歧義也併不需要向前査看. 沒有任何字符的編碼是其它字符編碼的子串, 或是其它編碼序列的字串, 因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可, 不用擔心前後的上下文會對蒐索結果産生榦擾. 同時UTF8編碼的順序和Unicode碼點的順序一致, 因此可以直接排序UTF8編碼序列. 同業也沒有嵌入的NUL(0)字節, 可以很好地兼容那些使用NUL作爲字符串結尾的編程語言.</p>
|
||||
<p>Go的源文件采用UTF8編碼, 併且Go處理UTF8編碼的文本也很齣色. unicode 包提供了諸多處理 rune 字符相關功能的函數函數(區分字母和數組, 或者是字母的大寫和小寫轉換等), unicode/utf8 包了提供了rune 字符序列的UTF8編碼和解碼的功能.</p>
|
||||
<p>有很多Unicode字符很難直接從鍵盤輸入, 併且很多字符有着相似的結構; 有一些甚至是不可見的字符. Go字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符. 有兩種形式, \uhhhh 對應16bit的碼點值, \Uhhhhhhhh 對應32bit的碼點值, 其中h是一個十六進製數字; 一般很少需要使用32bit的形式. 每一個對應碼點的UTF8編碼. 例如: 下面的字母串面值都表示相同的值:</p>
|
||||
</code></pre><p>變長的編碼無法直接通過索引來訪問第n個字符,但是UTF8編碼獲得了很多額外的優點。首先UTF8編碼比較緊湊,完全兼容ASCII碼,併且可以自動同步:它可以通過向前迴朔最多2個字節就能確定當前字符編碼的開始字節的位置。它也是一個前綴編碼,所以當從左向右解碼時不會有任何歧義也併不需要向前査看(譯註:像GBK之類的編碼,如果不知道起點位置則可能會出現歧義)。沒有任何字符的編碼是其它字符編碼的子串,或是其它編碼序列的字串,因此蒐索一個字符時隻要蒐索它的字節編碼序列卽可,不用擔心前後的上下文會對蒐索結果産生榦擾。同時UTF8編碼的順序和Unicode碼點的順序一致,因此可以直接排序UTF8編碼序列。同時因爲沒有嵌入的NUL(0)字節,可以很好地兼容那些使用NUL作爲字符串結尾的編程語言。</p>
|
||||
<p>Go語言的源文件采用UTF8編碼,併且Go語言處理UTF8編碼的文本也很出色。unicode包提供了諸多處理rune字符相關功能的函數(比如區分字母和數組,或者是字母的大寫和小寫轉換等),unicode/utf8包則提供了用於rune字符序列的UTF8編碼和解碼的功能。</p>
|
||||
<p>有很多Unicode字符很難直接從鍵盤輸入,併且還有很多字符有着相似的結構;有一些甚至是不可見的字符(譯註:中文和日文就有很多相似但不同的字)。Go語言字符串面值中的Unicode轉義字符讓我們可以通過Unicode碼點輸入特殊的字符。有兩種形式:\uhhhh對應16bit的碼點值,\Uhhhhhhhh對應32bit的碼點值,其中h是一個十六進製數字;一般很少需要使用32bit的形式。每一個對應碼點的UTF8編碼。例如:下面的字母串面值都表示相同的值:</p>
|
||||
<pre><code>"世界"
|
||||
"\xe4\xb8\x96\xe7\x95\x8c"
|
||||
"\u4e16\u754c"
|
||||
"\U00004e16\U0000754c"
|
||||
</code></pre><p>上面三個轉義序列爲第一個字符串提供替代寫法, 但是它們的值都是相同的.</p>
|
||||
<p>Unicode轉義也可以使用在rune字符中. 下面三個字符是等價的:</p>
|
||||
</code></pre><p>上面三個轉義序列都爲第一個字符串提供替代寫法,但是它們的值都是相同的。</p>
|
||||
<p>Unicode轉義也可以使用在rune字符中。下面三個字符是等價的:</p>
|
||||
<pre><code>'世' '\u4e16' '\U00004e16'
|
||||
</code></pre><p>對於小於256碼點值可以寫在一個十六進製轉義字節中, 例如 '\x41' 對應 'A' 字符, 但是對於更大的碼點則必鬚使用 \u 或 \U 轉義形式. 因此, '\xe4\xb8\x96' 併不是一個合法的rune字符, 雖然這三個字節對應一個有效的UTF8編碼的碼點.</p>
|
||||
<p>得意於UTF8優良的設計, 諸多字符串操作都不需要解碼. 我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:</p>
|
||||
</code></pre><p>對於小於256碼點值可以寫在一個十六進製轉義字節中,例如'\x41'對應字符'A',但是對於更大的碼點則必鬚使用\u或\U轉義形式。因此,'\xe4\xb8\x96'併不是一個合法的rune字符,雖然這三個字節對應一個有效的UTF8編碼的碼點。</p>
|
||||
<p>得益於UTF8編碼優良的設計,諸多字符串操作都不需要解碼操作。我們可以不用解碼直接測試一個字符串是否是另一個字符串的前綴:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> HasPrefix(s, prefix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(s) >= <span class="hljs-built_in">len</span>(prefix) && s[:<span class="hljs-built_in">len</span>(prefix)] == prefix
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者是後綴測試:</p>
|
||||
<p>或者是後綴測試:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> HasSuffix(s, suffix <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-built_in">len</span>(s) >= <span class="hljs-built_in">len</span>(suffix) && s[<span class="hljs-built_in">len</span>(s)-<span class="hljs-built_in">len</span>(suffix):] == suffix
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者是包含子串測試:</p>
|
||||
<p>或者是包含子串測試:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(s, substr <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span> {
|
||||
<span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(s); i++ {
|
||||
<span class="hljs-keyword">if</span> HasPrefix(s[i:], substr) {
|
||||
@@ -2123,72 +2127,72 @@ Usage:
|
||||
<span class="hljs-keyword">return</span> <span class="hljs-constant">false</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>對於UTF8編碼後文本的處理和原始的字節處理邏輯一樣. 但是對應很多其它編碼則併不是這樣的. (上面的函數都來自 strings 字符串處理包, 雖然它們的實現包含了一個用哈希技術優化的 Contains 實現.)</p>
|
||||
<p>另以方面, 如果我們眞的關心每個Unicode字符, 我們可以使用其它機製. 考慮前面的第一個例子中的字符串, 它包混合了中西兩種字符. 圖3.5展示了它的內存表示形式. 字符串包含13個字節, 以UTF8形式編碼, 但是隻對應9個Unicode字符:</p>
|
||||
<p>對於UTF8編碼後文本的處理和原始的字節處理邏輯是一樣的。但是對應很多其它編碼則併不是這樣的。(上面的函數都來自strings字符串處理包,眞實的代碼包含了一個用哈希技術優化的Contains 實現。)</p>
|
||||
<p>另一方面,如果我們眞的關心每個Unicode字符,我們可以使用其它處理方式。考慮前面的第一個例子中的字符串,它包混合了中西兩種字符。圖3.5展示了它的內存表示形式。字符串包含13個字節,以UTF8形式編碼,但是隻對應9個Unicode字符:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">import</span> <span class="hljs-string">"unicode/utf8"</span>
|
||||
|
||||
s := <span class="hljs-string">"Hello, 世界"</span>
|
||||
fmt.Println(<span class="hljs-built_in">len</span>(s)) <span class="hljs-comment">// "13"</span>
|
||||
fmt.Println(utf8.RuneCountInString(s)) <span class="hljs-comment">// "9"</span>
|
||||
</code></pre>
|
||||
<p>爲了處理這些眞實的字符, 我們需要一個UTF8解碼器. unicode/utf8 包提供了實現, 我們可以這樣使用:</p>
|
||||
<p>爲了處理這些眞實的字符,我們需要一個UTF8解碼器。unicode/utf8包提供了該功能,我們可以這樣使用:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-built_in">len</span>(s); {
|
||||
r, size := utf8.DecodeRuneInString(s[i:])
|
||||
fmt.Printf(<span class="hljs-string">"%d\t%c\n"</span>, i, r)
|
||||
i += size
|
||||
}
|
||||
</code></pre>
|
||||
<p>每一次調用 DecodeRuneInString 函數都返迴一個 r 和 長度, r 對應字符本身, 長度對應r采用UTF8編碼後的字節數目. 長度可以用於更新第i個字符在字符串中的字節索引位置. 但是這種方式是笨拙的, 我們需要更簡潔的語法. 幸運的是, Go的range循環在處理字符串的時候, 會自動隱式解碼UTF8字符串. 下面的循環運行如圖3.5所示; 需要註意的是對於非ASCII, 索引更新的步長超過1個字節.</p>
|
||||
<p>每一次調用DecodeRuneInString函數都返迴一個r和長度,r對應字符本身,長度對應r采用UTF8編碼後的編碼字節數目。長度可以用於更新第i個字符在字符串中的字節索引位置。但是這種編碼方式是笨拙的,我們需要更簡潔的語法。幸運的是,Go語言的range循環在處理字符串的時候,會自動隱式解碼UTF8字符串。下面的循環運行如圖3.5所示;需要註意的是對於非ASCII,索引更新的步長將超過1個字節。</p>
|
||||
<p><img src="../images/ch3-05.png" alt=""></p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">for</span> i, r := <span class="hljs-keyword">range</span> <span class="hljs-string">"Hello, 世界"</span> {
|
||||
fmt.Printf(<span class="hljs-string">"%d\t%q\t%d\n"</span>, i, r, r)
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們可以使用一個簡單的循環來統計字符串中字符的數目, 像這樣:</p>
|
||||
<p>我們可以使用一個簡單的循環來統計字符串中字符的數目,像這樣:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">for</span> _, _ = <span class="hljs-keyword">range</span> s {
|
||||
n++
|
||||
}
|
||||
</code></pre>
|
||||
<p>想其它形式的循環那樣, 我們可以忽略不需要的變量:</p>
|
||||
<p>像其它形式的循環那樣,我們也可以忽略不需要的變量:</p>
|
||||
<pre><code class="lang-Go">n := <span class="hljs-number">0</span>
|
||||
<span class="hljs-keyword">for</span> <span class="hljs-keyword">range</span> s {
|
||||
n++
|
||||
}
|
||||
</code></pre>
|
||||
<p>或者我們可以直接調用 utf8.RuneCountInString(s) 函數.</p>
|
||||
<p>正如我們前面提到了, 文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例, 這是正確的. 如果用於循環的字符串隻是一個普通的二進製數據, 或者是含有錯誤編碼的UTF8數據, 將會發送什麽?</p>
|
||||
<p>每一個UTF8字符解碼, 不管是顯示地調用 utf8.DecodeRuneInString 解碼或在 range 循環中隱式地解碼, 如果遇到一個錯誤的輸入字節, 將生成一個特别的Unicode字符 '\uFFFD', 在印刷中這個符號通常是一個黑色六角或鑽石形狀, 里面包含一個白色的問號(?). 當程序遇到這樣的一個字符, 通常是一個信號, 説明輸入併不是一個完美沒有錯誤的的UTF8編碼字符串.</p>
|
||||
<p>UTF8作爲交換格式是非常方便的, 但是在程序內部采用rune類型可能更方便, 因爲rune大小一致, 支持數組索引和方便切割.</p>
|
||||
<p>string 接受到 []rune 的轉換, 可以將一個UTF8編碼的字符串解碼爲Unicode字符序列:</p>
|
||||
<p>或者我們可以直接調用utf8.RuneCountInString(s)函數。</p>
|
||||
<p>正如我們前面提到的,文本字符串采用UTF8編碼隻是一種慣例,但是對於循環的眞正字符串併不是一個慣例,這是正確的。如果用於循環的字符串隻是一個普通的二進製數據,或者是含有錯誤編碼的UTF8數據,將會發送什麽呢?</p>
|
||||
<p>每一個UTF8字符解碼,不管是顯式地調用utf8.DecodeRuneInString解碼或是在range循環中隱式地解碼,如果遇到一個錯誤的UTF8編碼輸入,將生成一個特别的Unicode字符'\uFFFD',在印刷中這個符號通常是一個黑色六角或鑽石形狀,里面包含一個白色的問號(?)。當程序遇到這樣的一個字符,通常是一個危險信號,説明輸入併不是一個完美沒有錯誤的UTF8字符串。</p>
|
||||
<p>UTF8字符串作爲交換格式是非常方便的,但是在程序內部采用rune序列可能更方便,因爲rune大小一致,支持數組索引和方便切割。</p>
|
||||
<p>string接受到[]rune的類型轉換,可以將一個UTF8編碼的字符串解碼爲Unicode字符序列:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-comment">// "program" in Japanese katakana</span>
|
||||
s := <span class="hljs-string">"プログラム"</span>
|
||||
fmt.Printf(<span class="hljs-string">"% x\n"</span>, s) <span class="hljs-comment">// "e3 83 97 e3 83 ad e3 82 b0 e3 83 a9 e3 83 a0"</span>
|
||||
r := []<span class="hljs-typename">rune</span>(s)
|
||||
fmt.Printf(<span class="hljs-string">"%x\n"</span>, r) <span class="hljs-comment">// "[30d7 30ed 30b0 30e9 30e0]"</span>
|
||||
</code></pre>
|
||||
<p>(在第一個Printf中的 <code>% x</code> 參數用於在每個十六進製數字前插入一個空格.)</p>
|
||||
<p>如果是將一個 []rune 類型的Unicode字符切片或數組轉爲string, 則對它們進行UTF8編碼:</p>
|
||||
<p>(在第一個Printf中的<code>% x</code>參數用於在每個十六進製數字前插入一個空格。)</p>
|
||||
<p>如果是將一個[]rune類型的Unicode字符slice或數組轉爲string,則對它們進行UTF8編碼:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(r)) <span class="hljs-comment">// "プログラム"</span>
|
||||
</code></pre>
|
||||
<p>將一個整數轉型爲字符串意思是生成整數作爲Unicode碼點的UTF8編碼的字符串: </p>
|
||||
<p>將一個整數轉型爲字符串意思是生成以隻包含對應Unicode碼點字符的UTF8字符串:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">65</span>)) <span class="hljs-comment">// "A", not "65"</span>
|
||||
fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">0x4eac</span>)) <span class="hljs-comment">// "京"</span>
|
||||
</code></pre>
|
||||
<p>如果對應碼點的字符是無效的, 則用'\uFFFD'無效字符作爲替換:</p>
|
||||
<p>如果對應碼點的字符是無效的,則用'\uFFFD'無效字符作爲替換:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(<span class="hljs-typename">string</span>(<span class="hljs-number">1234567</span>)) <span class="hljs-comment">// "(?)"</span>
|
||||
</code></pre>
|
||||
<h3 id="354-字符串和byte切片">3.5.4. 字符串和Byte切片</h3>
|
||||
<p>標準庫中有四個包對字符串處理尤爲重要: bytes, strings, strconv, 和 unicode. strings 包提供了許多如字符串的査詢, 替換, 比較, 截斷, 拆分, 和合併等功能.</p>
|
||||
<p>bytes 包也提供了很多類似功能的函數, 但是針對和字符串有着相同結構的 []byte 類型. 因爲字符串是隻讀的, 因此逐步構建字符串會導致很多分配和複製. 在這種情況下, 使用 bytes.Buffer 類型會更有效, 稍後我們將展示.</p>
|
||||
<p>strconv 包提供了 布爾型, 整型數, 浮點數和對應字符串間的相互轉換, 還提供了雙引號的字符串面值形式的轉換.</p>
|
||||
<p>unicode 包提供了類似 IsDigit, IsLetter, IsUpper, 和 IsLower 等功能, 它們用於給字符分類. 每個函數有一個單一的rune類型的參數, 然後返迴一個布爾值. 像 ToUpper 和 ToLower 之類的轉換函數將用於rune字符的大小寫轉換. 所有的這些函數都是遵循Unicode標準定義的字母,數字等分類規范. strings 包也有類似的函數, 它們是 ToUpper 和 ToLower, 將原始字符串的每個字符都做相應的轉換, 然後返迴新的字符串.</p>
|
||||
<p>下面的 basename 函數的靈感由Unix shell的同名工具而來. 在我們實現的版本中, basename(s) 將看起來像是繫統路徑的前綴刪除, 同時將看似文件類型的後綴名刪除:</p>
|
||||
<p>標準庫中有四個包對字符串處理尤爲重要:bytes、strings、strconv和unicode包。strings包提供了許多如字符串的査詢、替換、比較、截斷、拆分和合併等功能。</p>
|
||||
<p>bytes包也提供了很多類似功能的函數,但是針對和字符串有着相同結構的[]byte類型。因爲字符串是隻讀的,因此逐步構建字符串會導致很多分配和複製。在這種情況下,使用bytes.Buffer類型將會更有效,稍後我們將展示。</p>
|
||||
<p>strconv包提供了布爾型、整型數、浮點數和對應字符串的相互轉換,還提供了雙引號轉義相關的轉換。</p>
|
||||
<p>unicode包提供了IsDigit、IsLetter、IsUpper和IsLower等類似功能,它們用於給字符分類。每個函數有一個單一的rune類型的參數,然後返迴一個布爾值。而像ToUpper和ToLower之類的轉換函數將用於rune字符的大小寫轉換。所有的這些函數都是遵循Unicode標準定義的字母、數字等分類規范。strings包也有類似的函數,它們是ToUpper和ToLower,將原始字符串的每個字符都做相應的轉換,然後返迴新的字符串。</p>
|
||||
<p>下面例子的basename函數靈感於Unix shell的同名工具。在我們實現的版本中,basename(s)將看起來像是繫統路徑的前綴刪除,同時將看似文件類型的後綴名部分刪除:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(basename(<span class="hljs-string">"a/b/c.go"</span>)) <span class="hljs-comment">// "c"</span>
|
||||
fmt.Println(basename(<span class="hljs-string">"c.d.go"</span>)) <span class="hljs-comment">// "c.d"</span>
|
||||
fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <span class="hljs-comment">// "abc"</span>
|
||||
</code></pre>
|
||||
<p>第一個版本併沒有使用任何庫, 全部手工實現:</p>
|
||||
<p>第一個版本併沒有使用任何庫,全部手工硬編碼實現:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/basename1
|
||||
<span class="hljs-comment">// basename removes directory components and a .suffix.</span>
|
||||
<span class="hljs-comment">// e.g., a => a, a.go => a, a/b/c.go => c, a/b.c.go => b.c</span>
|
||||
@@ -2210,7 +2214,7 @@ fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <sp
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
</code></pre>
|
||||
<p>一個簡化的版本使用了 strings.LastIndex 庫函數:</p>
|
||||
<p>簡化個版本使用了strings.LastIndex庫函數:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/basename2
|
||||
|
||||
<span class="hljs-keyword">func</span> basename(s <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span> {
|
||||
@@ -2222,8 +2226,8 @@ fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <sp
|
||||
<span class="hljs-keyword">return</span> s
|
||||
}
|
||||
</code></pre>
|
||||
<p>path 和 path/filepath 包提供了關於文件名更一般的函數操作. 使用斜槓分隔路徑可以在任何操作繫統上工作. 斜槓本身不應該用於文件名, 但是在其他一些領域可能是有效的, 例如URL路徑組件. 相比之下, path/filepath 包使用操作繫統本身的路徑規則, 例如 POSIX 繫統使用 /foo/bar, Microsoft Windows 使用 c:\foo\bar 等.</p>
|
||||
<p>讓我們繼續另一個字符串的例子. 任務是將一個表示整值的字符串, 每隔三個字符插入一個逗號, 例如 "12345" 處理後成爲 "12,345". 這個版本隻適用於整數類型; 支持浮點數類型的支持留做練習.</p>
|
||||
<p>path和path/filepath包提供了關於文件路徑名更一般的函數操作。使用斜槓分隔路徑可以在任何操作繫統上工作。斜槓本身不應該用於文件名,但是在其他一些領域可能會用於文件名,例如URL路徑組件。相比之下,path/filepath包則使用操作繫統本身的路徑規則,例如POSIX繫統使用/foo/bar,而Microsoft Windows使用c:\foo\bar等。</p>
|
||||
<p>讓我們繼續另一個字符串的例子。函數的功能是將一個表示整值的字符串,每隔三個字符插入一個逗號分隔符,例如“12345”處理後成爲“12,345”。這個版本隻適用於整數類型;支持浮點數類型的支持留作練習。</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/comma
|
||||
|
||||
<span class="hljs-comment">// comma inserts commas in a non-negative decimal integer string.</span>
|
||||
@@ -2235,15 +2239,15 @@ fmt.Println(basename(<span class="hljs-string">"abc"</span>)) <sp
|
||||
<span class="hljs-keyword">return</span> comma(s[:n-<span class="hljs-number">3</span>]) + <span class="hljs-string">","</span> + s[n-<span class="hljs-number">3</span>:]
|
||||
}
|
||||
</code></pre>
|
||||
<p>輸入 comma 的參數是一個字符串. 如果輸入字符串的長度小於或等於3的話, 則不需要插入逗號. 否則, comma 將在最後三個字符前切割爲兩個兩個子串, 然後用前面的子串遞歸調用自身.</p>
|
||||
<p>一個字符串包含的字節數組, 一旦創建, 是不可變的. 相比之下, 一個字節切片的原始則可以自由地脩改.</p>
|
||||
<p>字符串和字節切片可以相互轉換:</p>
|
||||
<p>輸入comma函數的參數是一個字符串。如果輸入字符串的長度小於或等於3的話,則不需要插入逗分隔符。否則,comma函數將在最後三個字符前位置將字符串切割爲兩個兩個子串併插入逗號分隔符,然後通過遞歸調用自身來出前面的子串。</p>
|
||||
<p>一個字符串是包含的隻讀字節數組,一旦創建,是不可變的。相比之下,一個字節slice的元素則可以自由地脩改。</p>
|
||||
<p>字符串和字節slice之間可以相互轉換:</p>
|
||||
<pre><code class="lang-Go">s := <span class="hljs-string">"abc"</span>
|
||||
b := []<span class="hljs-typename">byte</span>(s)
|
||||
s2 := <span class="hljs-typename">string</span>(b)
|
||||
</code></pre>
|
||||
<p>從概念上講, []byte(s) 轉換是分配了一個新的字節數組保存了字符串數據的拷貝, 然後引用這個字節數組. 編譯器的優化可以避免在一些場景下分配和複製字符串數據, 但總的來説需要確保在b被脩改的情況下, 原始的s字符串也不會改變. 將一個字節切片轉到字符串的 string(b) 操作則是構造一個拷貝, 以確保s2字符串是隻讀的.</p>
|
||||
<p>爲了避免轉換中不必要的內存分配, bytes包和strings同時提供了許多類似的實用函數. 下面是strings包中的六個函數:</p>
|
||||
<p>從概念上講,一個[]byte(s)轉換是分配了一個新的字節數組用於保存字符串數據的拷貝,然後引用這個底層的字節數組。編譯器的優化可以避免在一些場景下分配和複製字符串數據,但總的來説需要確保在變量b被脩改的情況下,原始的s字符串也不會改變。將一個字節slice轉到字符串的string(b)操作則是構造一個字符串拷貝,以確保s2字符串是隻讀的。</p>
|
||||
<p>爲了避免轉換中不必要的內存分配,bytes包和strings同時提供了許多實用函數。下面是strings包中的六個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(s, substr <span class="hljs-typename">string</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Count(s, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Fields(s <span class="hljs-typename">string</span>) []<span class="hljs-typename">string</span>
|
||||
@@ -2251,7 +2255,7 @@ s2 := <span class="hljs-typename">string</span>(b)
|
||||
<span class="hljs-keyword">func</span> Index(s, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Join(a []<span class="hljs-typename">string</span>, sep <span class="hljs-typename">string</span>) <span class="hljs-typename">string</span>
|
||||
</code></pre>
|
||||
<p>bytes 包中對應的六個函數:</p>
|
||||
<p>bytes包中也對應的六個函數:</p>
|
||||
<pre><code class="lang-Go"><span class="hljs-keyword">func</span> Contains(b, subslice []<span class="hljs-typename">byte</span>) <span class="hljs-typename">bool</span>
|
||||
<span class="hljs-keyword">func</span> Count(s, sep []<span class="hljs-typename">byte</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Fields(s []<span class="hljs-typename">byte</span>) [][]<span class="hljs-typename">byte</span>
|
||||
@@ -2259,8 +2263,8 @@ s2 := <span class="hljs-typename">string</span>(b)
|
||||
<span class="hljs-keyword">func</span> Index(s, sep []<span class="hljs-typename">byte</span>) <span class="hljs-typename">int</span>
|
||||
<span class="hljs-keyword">func</span> Join(s [][]<span class="hljs-typename">byte</span>, sep []<span class="hljs-typename">byte</span>) []<span class="hljs-typename">byte</span>
|
||||
</code></pre>
|
||||
<p>唯一的區别是字符串類型參數被替換成了字節切片類型的參數.</p>
|
||||
<p>bytes 包還提供了 Buffer 類型用於字節切片的緩存. 一個 Buffer 開始是空的, 但是隨着 string, byte, 和 []byte 等類型數據的寫入可以動態增長, 一個 bytes.Buffer 變量併不需要處理化, 因此零值也是有效的:</p>
|
||||
<p>它們之間唯一的區别是字符串類型參數被替換成了字節slice類型的參數。</p>
|
||||
<p>bytes包還提供了Buffer類型用於字節slice的緩存。一個Buffer開始是空的,但是隨着string、byte或[]byte等類型數據的寫入可以動態增長,一個bytes.Buffer變量併不需要處理化,因爲零值也是有效的:</p>
|
||||
<pre><code class="lang-Go">gopl.io/ch3/printints
|
||||
|
||||
<span class="hljs-comment">// intsToString is like fmt.Sprintf(values) but adds commas.</span>
|
||||
@@ -2281,30 +2285,30 @@ s2 := <span class="hljs-typename">string</span>(b)
|
||||
fmt.Println(intsToString([]<span class="hljs-typename">int</span>{<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>})) <span class="hljs-comment">// "[1, 2, 3]"</span>
|
||||
}
|
||||
</code></pre>
|
||||
<p>當向 bytes.Buffer 添加任意字符的UTF8編碼, 最好使用 bytes.Buffer 的 WriteRune 方法, 但是 WriteByte 方法對於寫入類似 '[' 和 ']' 等 ASCII 字符則更有效.</p>
|
||||
<p>bytes.Buffer 類型有着諸多實用的功能, 我們在第七章討論接口時層涉及到, 我們將看看如何將它用作一個I/O 的輸入和輸齣對象, 例如 Fprintf 的 io.Writer 輸齣, 或作爲輸入源 io.Reader.</p>
|
||||
<p><strong>練習3.10:</strong> 編寫一個非遞歸版本的comma函數, 使用 bytes.Buffer 代替字符串鏈接操作.</p>
|
||||
<p><strong>練習3.11:</strong> 完善 comma 函數, 以支持浮點數處理和一個可選的正負號處理.</p>
|
||||
<p><strong>練習3.12:</strong> 編寫一個函數, 判斷兩個字符串是否是是相互打亂的, 也就是説它們有着相同的字符, 但是對應不同的順序.</p>
|
||||
<p>當向bytes.Buffer添加任意字符的UTF8編碼時,最好使用bytes.Buffer的WriteRune方法,但是WriteByte方法對於寫入類似'['和']'等ASCII字符則會更加有效。</p>
|
||||
<p>bytes.Buffer類型有着很多實用的功能,我們在第七章討論接口時將會涉及到,我們將看看如何將它用作一個I/O的輸入和輸出對象,例如當做Fprintf的io.Writer輸出對象,或者當作io.Reader類型的輸入源對象。</p>
|
||||
<p><strong>練習 3.10:</strong> 編寫一個非遞歸版本的comma函數,使用bytes.Buffer代替字符串鏈接操作。</p>
|
||||
<p><strong>練習 3.11:</strong> 完善comma函數,以支持浮點數處理和一個可選的正負號的處理。</p>
|
||||
<p><strong>練習 3.12:</strong> 編寫一個函數,判斷兩個字符串是否是是相互打亂的,也就是説它們有着相同的字符,但是對應不同的順序。</p>
|
||||
<h3 id="355-字符串和數字的轉換">3.5.5. 字符串和數字的轉換</h3>
|
||||
<p>除了字符串, 字符, 字節 之間的轉換, 字符串和數值之間的轉換也比較常見. 由 strconv 包提供這類轉換功能.</p>
|
||||
<p>將一個整數轉爲字符串, 一種方法是用 fmt.Sprintf; 另一個方法是用 strconv.Itoa(“整數到ASCII”):</p>
|
||||
<p>除了字符串、字符、字節之間的轉換,字符串和數值之間的轉換也比較常見。由strconv包提供這類轉換功能。</p>
|
||||
<p>將一個整數轉爲字符串,一種方法是用fmt.Sprintf返迴一個格式化的字符串;另一個方法是用strconv.Itoa(“整數到ASCII”):</p>
|
||||
<pre><code class="lang-Go">x := <span class="hljs-number">123</span>
|
||||
y := fmt.Sprintf(<span class="hljs-string">"%d"</span>, x)
|
||||
fmt.Println(y, strconv.Itoa(x)) <span class="hljs-comment">// "123 123"</span>
|
||||
</code></pre>
|
||||
<p>FormatInt和FormatUint可以用不同的進製來格式化數字:</p>
|
||||
<p>FormatInt和FormatUint函數可以用不同的進製來格式化數字:</p>
|
||||
<pre><code class="lang-Go">fmt.Println(strconv.FormatInt(<span class="hljs-typename">int64</span>(x), <span class="hljs-number">2</span>)) <span class="hljs-comment">// "1111011"</span>
|
||||
</code></pre>
|
||||
<p>fmt.Printf 函數的 %b, %d, %u, 和 %x 等參數提供功能往往比strconv 包的 Format 函數方便很多, 特别是在需要包含附加信息的時候:</p>
|
||||
<p>fmt.Printf函數的%b、%d、%u和%x等參數提供功能往往比strconv包的Format函數方便很多,特别是在需要包含附加額外信息的時候:</p>
|
||||
<pre><code class="lang-Go">s := fmt.Sprintf(<span class="hljs-string">"x=%b"</span>, x) <span class="hljs-comment">// "x=1111011"</span>
|
||||
</code></pre>
|
||||
<p>如果要將一個字符串解析爲整數, 可以使用 strconv 包的 Atoi 或 ParseInt 函數, 還有用於解析無符號整數的 ParseUint 函數:</p>
|
||||
<p>如果要將一個字符串解析爲整數,可以使用strconv包的Atoi或ParseInt函數,還有用於解析無符號整數的ParseUint函數:</p>
|
||||
<pre><code class="lang-Go">x, err := strconv.Atoi(<span class="hljs-string">"123"</span>) <span class="hljs-comment">// x is an int</span>
|
||||
y, err := strconv.ParseInt(<span class="hljs-string">"123"</span>, <span class="hljs-number">10</span>, <span class="hljs-number">64</span>) <span class="hljs-comment">// base 10, up to 64 bits</span>
|
||||
</code></pre>
|
||||
<p>ParseInt 函數的第三個參數是用於指定整型數的大小; 例如16表示int16, 0則表示int. 在任何情況下, 返迴的結果 y 總是 int64 類型, 你可以通過強製類型轉換將它轉爲更小的整數類型.</p>
|
||||
<p>有時候也會使用 fmt.Scanf 來解析輸入的字符串和數字, 特别是當字符串和數字混合在一行的時候, 它可以靈活處理不完整或不規則的輸入.</p>
|
||||
<p>ParseInt函數的第三個參數是用於指定整型數的大小;例如16表示int16,0則表示int。在任何情況下,返迴的結果y總是int64類型,你可以通過強製類型轉換將它轉爲更小的整數類型。</p>
|
||||
<p>有時候也會使用fmt.Scanf來解析輸入的字符串和數字,特别是當字符串和數字混合在一行的時候,它可以靈活處理不完整或不規則的輸入。</p>
|
||||
|
||||
|
||||
</section>
|
||||
@@ -2336,7 +2340,7 @@ y, err := strconv.ParseInt(<span class="hljs-string">"123"</span>, <sp
|
||||
|
||||
<script>
|
||||
require(["gitbook"], function(gitbook) {
|
||||
var config = {"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
var config = {"katex":{},"highlight":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"fontsettings":{"theme":"white","family":"sans","size":2}};
|
||||
gitbook.start(config);
|
||||
});
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user