返回文章列表

Dart 字串與 Unicode 處理技巧

本文深入探討 Dart 字串處理,特別針對 Unicode 字元、代理對、Rune 以及圖形叢集的應用,解析 Dart 如何處理複雜的 Unicode 字元,並使用 characters 套件精確計算字串長度,同時示範基本字串操作、多行字串、原始字串等技巧,提供 Dart 字串處理的全面。

程式語言 Web 開發

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);

這個範例會輸出一個多行字串。

練習

  1. 建立兩個字串常數 firstNamelastName,分別初始化為您的名字和姓氏。
  2. 使用插值建立一個字串常數 fullName,包含 firstNamelastName,中間用空格分隔。
  3. 使用插值建立一個字串常數 myDetails,包含 fullName,以介紹自己。例如: “Hello, my name is Ray Wenderlich.”

內容解密:

  • characters 屬性可以取得字串的長度。
  • 使用 + 運運算元可以連線字串。
  • 使用 ${} 來插入變數。
  • 三個單引號或三個雙引號可以定義多行字串。

圖表翻譯:

這個圖表展示了字串的基本操作,包括連線、插值和多行字串。

字串基礎

在 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 程式碼來插入特定的字元。

圖表翻譯:

此圖表展示了 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('差');
}

內容解密:

上述程式碼使用 ifelse 語句來控制程式的流程。如果 score 大於或等於 90,則印出「優」;如果 score 大於或等於 80,但小於 90,則印出「良」;否則印出「差」。

圖表翻譯:

上述圖表展示了程式的控制流程。根據 score 的值,程式會執行不同的動作。

布林值與比較運運算元

布林值(Boolean)是一種基本的資料型態,代表真或假的邏輯值。在 Dart 中,你可以使用 truefalse 關鍵字來設定布林值的狀態。例如:

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 中使用布林值和比較運運算元。比較運運算元可以用來測試兩個值是否相等、不相等、大於或小於。邏輯運運算元 ! 可以用來反轉布林值。這些運運算元是程式設計中非常重要的工具,可以用來控制程式的流程和邏輯。

圖表翻譯:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Dart 字串與 Unicode 處理技巧

package "圖論網路分析" {
    package "節點層" {
        component [節點 A] as nodeA
        component [節點 B] as nodeB
        component [節點 C] as nodeC
        component [節點 D] as nodeD
    }

    package "中心性指標" {
        component [度中心性
Degree Centrality] as degree
        component [特徵向量中心性
Eigenvector Centrality] as eigen
        component [介數中心性
Betweenness Centrality] as between
        component [接近中心性
Closeness Centrality] as close
    }
}

nodeA -- nodeB
nodeA -- nodeC
nodeB -- nodeD
nodeC -- nodeD

nodeA --> degree : 計算連接數
nodeA --> eigen : 計算影響力
nodeB --> between : 計算橋接度
nodeC --> close : 計算距離

note right of degree
  直接連接數量
  衡量局部影響力
end note

note right of eigen
  考慮鄰居重要性
  衡量全局影響力
end note

@enduml

此圖表示了布林值和比較運運算元的流程,從設定布林值開始,到比較運運算元、測試相等、測試不相等、邏輯運運算元,最後傳回布林值結果。

條件控制與邏輯運算

在程式設計中,條件控制和邏輯運算是非常重要的概念。它們允許我們根據不同的條件執行不同的動作或做出不同的決定。

條件運運算元

條件運運算元用於比較兩個值之間的關係。常見的條件運運算元包括:

  • ==:等於
  • !=:不等於
  • >:大於
  • <:小於
  • >=:大於或等於
  • <=:小於或等於

例如:

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;

這個複雜條件可以分解成以下步驟:

  1. 3 > 4:假
  2. 1 < 2:真
  3. 3 > 4 && 1 < 2:假(因為 3 > 4 為假)
  4. 1 < 4:真
  5. 假 || 真:真

因此,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'

練習

  1. 建立一個名為 myAge 的常數,設定為你的年齡。然後,建立一個名為 isTeenager 的常數,使用布林邏輯來判斷你的年齡是否在 13 到 19 之間。
  2. 建立一個名為 maryAge 的常數,設定為 30。然後,建立一個名為 bothTeenagers 的常數,使用布林邏輯來判斷你和 Mary 是否都是青少年。
  3. 建立一個名為 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),可以是 truefalse。如果條件為 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);
  }
}

在這個例子中,localinsideIf 只能在 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';
}

練習

  1. 建立一個名為 myAge 的常數,並初始化它為你的年齡。寫一個 if 陳述式,若你的年齡在 13 到 19 之間,則輸出 “Teenager”,否則輸出 “Not a teenager”。
  2. 使用三元運運算元替代 if-else 陳述式,並將結果設為一個名為 answer 的變數。

從使用者體驗的最佳化角度,Dart 字串處理的機制,特別是圖形叢集的處理方式,對開發者和終端使用者都產生了實質影響。深入剖析 Dart 的字串表示方式,可以發現其從底層的 UTF-16 編碼到高階的 characters 套件,都體現了對 Unicode 正確性和使用者體驗的重視。分析 Dart 如何處理代理對、Rune 和圖形叢集,以及 characters 套件如何簡化字串長度計算,可以看出 Dart 致力於提供更直觀、更符合 Unicode 標準的字串操作方式。然而,開發者仍需注意 UTF-16 編碼單元和圖形叢集之間的差異,避免在字串操作中產生錯誤。對於需要處理複雜 Unicode 字元的應用程式,characters 套件的應用至關重要。玄貓認為,Dart 的字串處理機制,特別是 characters 套件的引入,代表了程式語言在處理 Unicode 字串方面的最佳實務,值得其他語言借鑒。隨著 Unicode 標準的持續演進,我們預見 Dart 也將持續最佳化其字串處理機制,以提供更便捷、更強大的功能。