Dart 的字串處理能力對於開發者來說至關重要,尤其在處理多語言文字和特殊符號時,理解 Unicode 的編碼機制以及 Dart 如何處理這些字元變得尤為重要。代理對和 Rune 的概念能幫助我們理解 Dart 如何表示超出基本多文種平面(BMP)的 Unicode 字元。更進一步,圖形叢集的概念則解決了多個 Unicode 字元組成單一顯示字元的問題,這在處理表情符號、旗幟等複雜字元時至關重要。使用 characters
套件能確保我們準確計算這些字元的數量,避免因為 UTF-16 編碼單元和實際顯示字元數量不一致而導致的錯誤。
代理對與Unicode碼點
代理對是一種使用兩個16位元的編碼單元來表示一個Unicode碼點的方法。例如,Unicode碼點127919可以使用代理對(55356, 57263)來表示。
const dart = ' ';
// [55356, 57263]
Rune與Unicode碼點
Dart提供了一種更方便的方式來處理Unicode碼點,稱為Rune。Rune是一個可以直接表示Unicode碼點的物件。
const dart = ' ';
print(dart.runes); // (127919)
Unicode圖形叢集
但是,Unicode圖形叢集(Grapheme Cluster)是一個更複雜的問題。圖形叢集是指多個Unicode碼點組合成一個單一的字元。例如,蒙古國旗可以使用兩個Unicode碼點(127474, 127475)來表示。
const flag = ' ';
print(flag.runes); // (127474, 127475)
家庭圖示的圖形叢集
另一個例子是家庭圖示,可以使用七個Unicode碼點(128104, 8205, 128105, 8205, 128103, 8205, 128102)來表示。
const family = ' ';
print(family.runes); // (128104, 8205, 128105, 8205, 128103, 8205, 128102)
字串長度與圖形叢集
計算字串長度時,需要考慮圖形叢集。使用length
屬性可以得到UTF-16編碼單元的數量,但這可能不是預期的結果。
const family = ' ';
family.length; // 11
family.runes.length; // 7
使用Characters套件處理圖形叢集
Dart提供了一個名為characters
的套件,可以用來處理圖形叢集。首先,需要在pubspec.yaml
檔案中新增套件依賴。
dependencies:
characters: ^1.2.1
然後,需要匯入套件並使用其功能。
import 'package:characters/characters.dart';
圖形叢集的應用
使用characters
套件,可以正確地處理圖形叢集,並得到預期的結果。
const family = ' ';
print(family.characters.length); // 1
字串基礎
在 Dart 中,字串是一種基本的資料型別,使用雙引號或單引號來定義。下面是一個簡單的字串範例:
const family = '👪';
print(family.characters.length); // 1
這個範例使用 characters
屬性來取得字串的長度,結果為 1,因為 👪
是一個單一的 Unicode 字元。
單引號與雙引號
Dart 允許使用單引號或雙引號來定義字串,兩者都可以使用:
const str1 = 'Hello';
const str2 = "Hello";
雖然 Dart 沒有強制規定使用哪一種引號,但 Flutter 的風格建議使用單引號,因此本文也會遵循這個慣例。
連線字串
連線字串是指將兩個或多個字串合併成一個新的字串。在 Dart 中,可以使用 +
運運算元來連線字串:
const str1 = 'Hello';
const str2 = ' World';
const result = str1 + str2; // 'Hello World'
也可以使用 ${}
來插入變數:
const name = 'Ray';
const introduction = 'Hello, my name is $name'; // 'Hello, my name is Ray'
多行字串
Dart 支援多行字串,可以使用三個單引號或三個雙引號來定義:
const bigString = '''
You can have a string
that contains multiple
lines
by using triple quotes.
''';
print(bigString);
這個範例會輸出一個多行字串。
練習
- 建立兩個字串常數
firstName
和lastName
,分別初始化為您的名字和姓氏。 - 使用插值建立一個字串常數
fullName
,包含firstName
和lastName
,中間用空格分隔。 - 使用插值建立一個字串常數
myDetails
,包含fullName
,以介紹自己。例如: “Hello, my name is Ray Wenderlich.”
內容解密:
characters
屬性可以取得字串的長度。- 使用
+
運運算元可以連線字串。 - 使用
${}
來插入變數。 - 三個單引號或三個雙引號可以定義多行字串。
圖表翻譯:
graph LR A[字串] --> B[連線] B --> C[插值] C --> D[多行字串] D --> E[輸出]
這個圖表展示了字串的基本操作,包括連線、插值和多行字串。
字串基礎
在 Dart 中,字串可以使用單引號或雙引號來定義。另外,還有一種多行字串的定義方式,使用三個單引號或三個雙引號。
// 單行字串
String singleLine = 'Hello, World!';
// 多行字串
String multiLine = '''這是一個多行字串
可以包含多行文字
並保留換行符''';
print(multiLine);
字串連線
Dart 提供了多種方式來連線字串。最簡單的方式是使用 +
運算子。
String hello = 'Hello, ';
String world = 'World!';
String result = hello + world;
print(result); // 輸出:Hello, World!
原始字串
如果你想要忽略字串中的特殊字元,可以使用原始字串。原始字串使用 r
字首來定義。
String rawString = r'My name \n is $name.';
print(rawString); // 輸出:My name \n is $name.
Unicode 字元
你可以使用 Unicode 程式碼來插入特定的字元。使用 \u
後面跟著四個十六進位制數字即可。
print('I \u2764 Dart\u0021'); // 輸出:
對於超過 FFFF
的 Unicode 程式碼,需要使用大括號 {}
包圍。
內容解密:
上述程式碼展示瞭如何在 Dart 中使用字串。首先,我們定義了一個單行字串和一個多行字串,然後展示瞭如何使用 +
運算子來連線字串。接下來,我們介紹了原始字串的概念,使用 r
字首來忽略特殊字元。最後,我們展示瞭如何使用 Unicode 程式碼來插入特定的字元。
圖表翻譯:
graph LR A[字串定義] --> B[單行字串] A --> C[多行字串] B --> D[使用 + 連線] C --> E[原始字串] E --> F[忽略特殊字元] D --> G[Unicode 字元] G --> H[插入特定字元]
此圖表展示了 Dart 中字串的不同定義方式和操作,包括單行字串、多行字串、連線、原始字串和 Unicode 字元的使用。
控制流和布林值
在計算機程式設計中,控制流是指程式在不同情況下執行不同的動作。例如,一個計算器程式在使用者按下加號按鈕時執行加法運算,在按下減號按鈕時執行減法運算。
布林值
布林值(Boolean)是一種資料型別,僅能夠儲存兩種狀態:true
(真)和false
(假)。這種資料型別非常適合用於儲存是或否的答案。
bool isOpen = true;
bool isFlying = false;
在 Dart 中,布林值可以用於控制程式的流程。例如,當某個條件為 true
時,執行某段程式碼;當某個條件為 false
時,執行另一段程式碼。
比較運算
比較運算是用於比較兩個值是否相等或不相等。Dart 中的比較運算子包括:
==
:相等!=
:不相等>
:大於<
:小於>=
:大於或相等<=
:小於或相等
bool isEqual = 5 == 5; // true
bool isNotEqual = 5 != 3; // true
bool isGreater = 5 > 3; // true
bool isLess = 5 < 3; // false
邏輯運算
邏輯運算是用於組合多個條件。Dart 中的邏輯運算子包括:
&&
:與(and)||
:或(or)!
:非(not)
bool isValid = 5 > 3 && 5 < 10; // true
bool isInvalid = 5 > 3 || 5 < 0; // true
bool isNotValid = !(5 > 3); // false
控制流程
控制流程是用於控制程式的執行順序。Dart 中的控制流程語句包括:
if
:如果條件為真,則執行某段程式碼else
:如果條件為假,則執行某段程式碼switch
:根據某個值執行不同的程式碼
int score = 85;
if (score >= 90) {
print('優');
} else if (score >= 80) {
print('良');
} else {
print('差');
}
內容解密:
上述程式碼使用 if
和 else
語句來控制程式的流程。如果 score
大於或等於 90,則印出「優」;如果 score
大於或等於 80,但小於 90,則印出「良」;否則印出「差」。
圖表翻譯:
graph LR A[開始] --> B{score >= 90} B -->|true| C[印出「優」] B -->|false| D{score >= 80} D -->|true| E[印出「良」] D -->|false| F[印出「差」]
上述圖表展示了程式的控制流程。根據 score
的值,程式會執行不同的動作。
布林值與比較運運算元
布林值(Boolean)是一種基本的資料型態,代表真或假的邏輯值。在 Dart 中,你可以使用 true
和 false
關鍵字來設定布林值的狀態。例如:
const bool yes = true;
const bool no = false;
由於 Dart 的型別推斷,你也可以省略型別注釋:
const yes = true;
const no = false;
比較運運算元
布林值常用於比較值。例如,你可能有兩個值,想要知道它們是否相等。這時候,你可以使用比較運運算元來進行比較。
測試相等
你可以使用相等運運算元 ==
來測試兩個值是否相等。例如:
const doesOneEqualTwo = (1 == 2);
Dart 會推斷 doesOneEqualTwo
是一個布林值。由於 1 不等於 2,所以 doesOneEqualTwo
會是 false
。你可以使用 print
函式來確認結果:
print(doesOneEqualTwo);
測試不相等
你也可以使用不相等運運算元 !=
來測試兩個值是否不相等。例如:
const doesOneNotEqualTwo = (1 != 2);
這次,比較的結果是 true
,因為 1 不等於 2。
邏輯運運算元
邏輯運運算元 !
可以用來反轉布林值。例如:
const alsoTrue = !(1 == 2);
由於 1 不等於 2,所以 (1 == 2)
是 false
,然後 !
運運算元將其反轉為 true
。
測試大於和小於
你可以使用大於運運算元 >
和小於運運算元 <
來比較兩個值。例如:
const isOneGreaterThanTwo = (1 > 2);
const isOneLessThanTwo = (1 < 2);
這些運運算元可以用來比較數值,並傳回布林值結果。
內容解密:
以上程式碼示範瞭如何在 Dart 中使用布林值和比較運運算元。比較運運算元可以用來測試兩個值是否相等、不相等、大於或小於。邏輯運運算元 !
可以用來反轉布林值。這些運運算元是程式設計中非常重要的工具,可以用來控制程式的流程和邏輯。
圖表翻譯:
flowchart TD A[開始] --> B[設定布林值] B --> C[比較運運算元] C --> D[測試相等] D --> E[測試不相等] E --> F[邏輯運運算元] F --> G[測試大於和小於] G --> H[傳回布林值結果]
此圖表示了布林值和比較運運算元的流程,從設定布林值開始,到比較運運算元、測試相等、測試不相等、邏輯運運算元,最後傳回布林值結果。
條件控制與邏輯運算
在程式設計中,條件控制和邏輯運算是非常重要的概念。它們允許我們根據不同的條件執行不同的動作或做出不同的決定。
條件運運算元
條件運運算元用於比較兩個值之間的關係。常見的條件運運算元包括:
==
:等於!=
:不等於>
:大於<
:小於>=
:大於或等於<=
:小於或等於
例如:
print(1 == 2); // false
print(1 != 2); // true
print(1 > 2); // false
print(1 < 2); // true
print(1 >= 2); // false
print(1 <= 2); // true
邏輯運算
邏輯運算用於組合多個條件。常見的邏輯運運算元包括:
&&
:與(AND)||
:或(OR)
與(AND)
與運運算元用於檢查兩個條件是否都為真。如果兩個條件都為真,則結果為真;否則,結果為假。
例如:
const isSunny = true;
const isFinished = true;
const willGoCycling = isSunny && isFinished;
print(willGoCycling); // true
或(OR)
或運運算元用於檢查兩個條件是否至少有一個為真。如果至少有一個條件為真,則結果為真;否則,結果為假。
例如:
const willTravelToAustralia = true;
const canFindPhoto = false;
const canDrawPlatypus = willTravelToAustralia || canFindPhoto;
print(canDrawPlatypus); // true
運運算元優先順序
當多個條件和邏輯運運算元一起使用時,需要注意運運算元優先順序。一般來說,條件運運算元優先於邏輯運運算元。
例如:
const andTrue = 1 < 2 && 4 > 3;
const andFalse = 1 < 2 && 3 > 4;
const orTrue = 1 < 2 || 3 > 4;
const orFalse = 1 == 2 || 3 == 4;
複雜條件
可以使用多個條件和邏輯運運算元組合成複雜條件。
例如:
const complexCondition = 3 > 4 && 1 < 2 || 1 < 4;
這個複雜條件可以分解成以下步驟:
3 > 4
:假1 < 2
:真3 > 4 && 1 < 2
:假(因為3 > 4
為假)1 < 4
:真假 || 真
:真
因此,complexCondition
的結果為真。
條件判斷與邏輯運算
在 Dart 中,條件判斷和邏輯運算是控制程式流程的重要工具。讓我們來看看如何使用這些工具。
邏輯運算
Dart 中的邏輯運算包括 &&
(與)、||
(或) 和 !
(非)。這些運算可以用來組合條件判斷。
例如,下面的程式碼使用 &&
和 ||
來判斷一個布林值:
bool result = false && true || true;
print(result); // true
在這個例子中,false && true
先被評估為 false
,然後 false || true
被評估為 true
。
運算優先順序
Dart 中的運算優先順序是指運算的執行順序。當多個運算同時出現時,優先順序最高的運算先被執行。以下是 Dart 中的運算優先順序列表:
!
(非)&&
(與)||
(或)
使用括號可以覆蓋運算優先順序。例如:
bool result = (3 > 4 && 1 < 2) || 1 < 4;
print(result); // true
在這個例子中,括號內的運算先被執行,然後結果被用來評估外面的運算。
字串比較
Dart 中可以使用 ==
運算子來比較兩個字串是否相等。例如:
const guess = 'dog';
const guessEqualsCat = guess == 'cat';
print(guessEqualsCat); // false
在這個例子中,guessEqualsCat
是一個布林值,表示 guess
是否等於 'cat'
。
練習
- 建立一個名為
myAge
的常數,設定為你的年齡。然後,建立一個名為isTeenager
的常數,使用布林邏輯來判斷你的年齡是否在 13 到 19 之間。 - 建立一個名為
maryAge
的常數,設定為 30。然後,建立一個名為bothTeenagers
的常數,使用布林邏輯來判斷你和 Mary 是否都是青少年。 - 建立一個名為
reader
的字串常數,設定為你的名字。建立另一個名為ray
的字串常數,設定為'Ray Wenderlich'
。建立一個名為rayIsReader
的布林常數,使用字串比較來判斷reader
是否等於ray
。
If 陳述式
If 陳述式是控制程式流程的第一種方式。它允許程式在某個條件為真時執行某個動作。例如:
if (2 > 1) {
print('Yes, 2 is greater than 1.');
}
這是一個簡單的 If 陳述式。條件是布林表示式,總是會被評估為真或假。如果條件為真,程式會執行 If 陳述式內的程式碼。
條件判斷的基礎
在 Dart 中,if
陳述式是一種基本的控制結構,允許您根據特定條件執行不同的程式碼。基本語法如下:
if (條件) {
// 條件成立時執行的程式碼
}
這裡,條件
是一個布林值(Boolean),可以是 true
或 false
。如果條件為 true
,則執行大括號內的程式碼。
Else 子句
您可以使用 else
子句來指定當條件不成立時要執行的程式碼。語法如下:
if (條件) {
// 條件成立時執行的程式碼
} else {
// 條件不成立時執行的程式碼
}
例如:
const animal = 'Fox';
if (animal == 'Cat' || animal == 'Dog') {
print('Animal is a house pet.');
} else {
print('Animal is not a house pet.');
}
在這個例子中,如果 animal
等於 'Cat'
或 'Dog'
,則執行第一個程式碼塊;否則,執行 else
子句中的程式碼。
Else-If 鏈
您可以使用 else if
來檢查多個條件。語法如下:
if (條件1) {
// 條件1成立時執行的程式碼
} else if (條件2) {
// 條件1不成立,但條件2成立時執行的程式碼
} else {
// 條件1和條件2都不成立時執行的程式碼
}
例如:
const trafficLight = 'yellow';
if (trafficLight == 'red') {
print('Stop');
} else if (trafficLight == 'yellow') {
print('Slow down');
} else if (trafficLight == 'green') {
print('Go');
} else {
print('INVALID COLOR!');
}
在這個例子中,程式會先檢查 trafficLight
是否等於 'red'
,如果不是,則檢查是否等於 'yellow'
,如果還不是,則檢查是否等於 'green'
。如果都不是,則執行 else
子句中的程式碼。
變數範圍
if
陳述式還引入了一個新的概念:範圍(Scope)。範圍是指變數可以被存取的範圍。在 Dart 中,範圍是使用大括號 {}
來定義的。如果您在大括號內定義了一個變數,則該變數只能在該大括號內使用。
void main() {
const local = 'Hello, main';
if (2 > 1) {
const insideIf = 'Hello, anybody?';
print(local);
print(insideIf);
}
}
在這個例子中,local
和 insideIf
只能在 main
函式內使用。如果您試圖在 main
函式外使用它們,將會出現錯誤。
控制流程
控制流程是指程式執行的順序和流程。Dart 中的控制流程可以透過 if 陳述式、switch 陳述式和迴圈來實作。
變數範圍
在 Dart 中,變數的範圍是指變數可以被存取的區域。變數可以被定義在不同的範圍中,包括全域、區域和塊範圍。
- 全域變數:定義在函式外部的變數,對於整個程式都是可見的。
- 區域變數:定義在函式內部的變數,只能在函式內部被存取。
- 塊範圍變數:定義在塊內部的變數,只能在塊內部被存取。
三元運運算元
三元運運算元是一種簡單的 if-else 陳述式,可以用來簡化程式碼。三元運運算元的語法如下:
(condition) ? valueIfTrue : valueIfFalse;
三元運運算元可以用來替代簡單的 if-else 陳述式,例如:
const score = 83;
String message;
if (score >= 60) {
message = 'You passed';
} else {
message = 'You failed';
}
可以簡化為:
const score = 83;
const message = (score >= 60) ? 'You passed' : 'You failed';
Switch 陳述式
Switch 陳述式是一種用來處理多個條件的控制流程陳述式。Switch 陳述式的語法如下:
switch (variable) {
case value1:
// code
break;
case value2:
// code
break;
...
default:
// code
}
Switch 陳述式可以用來替代多個 if-else 陳述式,例如:
int day = 2;
String dayName;
if (day == 1) {
dayName = 'Monday';
} else if (day == 2) {
dayName = 'Tuesday';
} else if (day == 3) {
dayName = 'Wednesday';
} else {
dayName = 'Unknown';
}
可以簡化為:
int day = 2;
String dayName;
switch (day) {
case 1:
dayName = 'Monday';
break;
case 2:
dayName = 'Tuesday';
break;
case 3:
dayName = 'Wednesday';
break;
default:
dayName = 'Unknown';
}
練習
- 建立一個名為
myAge
的常數,並初始化它為你的年齡。寫一個 if 陳述式,若你的年齡在 13 到 19 之間,則輸出 “Teenager”,否則輸出 “Not a teenager”。 - 使用三元運運算元替代 if-else 陳述式,並將結果設為一個名為
answer
的變數。
從使用者體驗的最佳化角度,Dart 字串處理的機制,特別是圖形叢集的處理方式,對開發者和終端使用者都產生了實質影響。深入剖析 Dart 的字串表示方式,可以發現其從底層的 UTF-16 編碼到高階的 characters
套件,都體現了對 Unicode 正確性和使用者體驗的重視。分析 Dart 如何處理代理對、Rune 和圖形叢集,以及 characters
套件如何簡化字串長度計算,可以看出 Dart 致力於提供更直觀、更符合 Unicode 標準的字串操作方式。然而,開發者仍需注意 UTF-16 編碼單元和圖形叢集之間的差異,避免在字串操作中產生錯誤。對於需要處理複雜 Unicode 字元的應用程式,characters
套件的應用至關重要。玄貓認為,Dart 的字串處理機制,特別是 characters
套件的引入,代表了程式語言在處理 Unicode 字串方面的最佳實務,值得其他語言借鑒。隨著 Unicode 標準的持續演進,我們預見 Dart 也將持續最佳化其字串處理機制,以提供更便捷、更強大的功能。