# Strings

Course: [Dart](https://www.devtutorials.ai/learn/dart) · Section: Strings
Source: https://www.devtutorials.ai/learn/dart/strings-and-dates/strings

> How to create strings, put values inside them, compare, search, cut, change, split and join them, build long text with StringBuffer, and handle Unicode and emoji.

## What Is a String?

**String** = a piece of text: letters, digits, spaces, symbols and emoji, in order.

For example:

- a name: `'Asha'`
- a sentence: `'Today is Sports Day.'`
- digits you never do maths on: `'600001'`

Each character has a position, starting at 0, just like a list (see **Lists**).

**Simple lesson:** a String is text, and Dart gives you many tools to search, cut and change it.

### A Real-World Example

The school app shows "Welcome, ASHA!" on the home screen.

- The name comes from the database as `'  asha '`, with extra spaces.
- The app **trims** the spaces, makes it **upper case**, and puts it **inside** a sentence.

Three string tools, one line of code. Try the tools yourself:

Every **Run** box on this page runs on the course's Dart runner in your browser.

Strings never change in place. Every method gives you a **new** string.

## What You Will Learn

In this lesson, you will learn:

1. How to create strings, including multi-line and raw strings.
2. String properties, interpolation, joining and comparing.
3. How to search, extract and change parts of a string.
4. How to change case, trim, split and join.
5. How to build long text with `StringBuffer`, and handle Unicode and emoji.

## String Fundamentals

### Creating Strings

```dart runnable title="Single, double, multi-line and raw"
void main() {
  var school = 'Green Valley';
  var quote = "It's Sports Day!";
  var address = '''Green Valley School
12, MG Road
Pune''';
  var path = r'C:\notes\new $file';
  print(school);
  print(quote);
  print(address);
  print(path);
}
```

**Escape sequence** = a backslash code for a special character, like `\n` (new line), `\t` (tab), `\'` (a quote) or `\$` (a dollar sign).

**Simple lesson:** use single quotes normally, three quotes for many lines, and `r''` to keep backslashes.

## String Properties

| Property | Gives | `'Asha'` gives |
|---|---|---|
| `length` | number of UTF-16 units | `4` |
| `isEmpty` | `true` if it is `''` | `false` |
| `isNotEmpty` | `true` if it has text | `true` |
| `codeUnits` | the number code of each unit | `[65, 115, 104, 97]` |
| `runes` | the Unicode code of each character | `(65, 115, 104, 97)` |

```dart runnable title="String properties"
void main() {
  var name = 'Asha';
  print(name.length);
  print(name.isEmpty);
  print(''.isEmpty);
  print(name.codeUnits);
  print(name[0]);
  print(name[name.length - 1]);
}
```

`name[0]` gives the first character. The last one is at `length - 1`.

**Simple lesson:** `length` counts the characters, and `[i]` reads one character.

## String Interpolation

**String interpolation** = putting a value inside a string.

- **`$name`** = put a variable's value here.
- **`${expression}`** = work out an expression, then put its value here.

```dart runnable title="$ and ${}"
void main() {
  var name = 'Ravi';
  var maths = 88;
  var science = 91;
  print('Hello, $name!');
  print('Total: ${maths + science}');
  print('Name in capitals: ${name.toUpperCase()}');
  print('Price: \$5');
}
```

- `$name.toUpperCase()` would NOT work. Dart would put `name` in, then print `.toUpperCase()` as text. Use `${...}` for anything more than a plain name.
- `\$` prints a real dollar sign.

**Simple lesson:** use `$name` for a plain variable and `${...}` for anything more.

## Concatenation

**Concatenation** = joining strings end to end.

- **`+`** joins two strings: `'Green' + ' Valley'`.
- **Adjacent string literals** = two literals written side by side are joined automatically.

```dart runnable title="Joining strings"
void main() {
  var first = 'Green';
  var full = first + ' ' + 'Valley';
  print(full);

  var notice = 'School will close early today '
      'because of the rain.';
  print(notice);

  var marks = 92;
  print('Marks: ' + marks.toString());
}
```

`+` only joins Strings. A number needs `toString()`, or better, interpolation: `'Marks: $marks'`.

**Simple lesson:** prefer interpolation, use `+` for short joins, and split long text into adjacent literals.

## String Comparison

- **`==`** = are the two strings exactly the same? Capital letters matter.
- **`compareTo()`** = which comes first in dictionary order? Gives `-1`, `0` or `1`.

```dart runnable title="Comparing strings"
void main() {
  print('Asha' == 'Asha');
  print('Asha' == 'asha');
  print('apple'.compareTo('banana'));
  print('mango'.compareTo('apple'));
  print('kiwi'.compareTo('kiwi'));
  print('asha'.toLowerCase() == 'ASHA'.toLowerCase());
}
```

To compare without caring about case, change both to lower case first.

**Simple lesson:** `==` checks exact equality, and `compareTo` gives dictionary order.

## Searching Strings

| Method | Question |
|---|---|
| `contains(x)` | is `x` anywhere inside? |
| `startsWith(x)` | does it begin with `x`? |
| `endsWith(x)` | does it end with `x`? |
| `indexOf(x)` | where is the first `x`? `-1` if missing |
| `lastIndexOf(x)` | where is the last `x`? |

```dart runnable title="Searching inside text"
void main() {
  var email = 'asha.k@greenvalley.edu';
  print(email.contains('@'));
  print(email.startsWith('asha'));
  print(email.endsWith('.edu'));
  print(email.indexOf('@'));
  print(email.lastIndexOf('.'));
  print(email.indexOf('#'));
}
```

**Simple lesson:** `contains`, `startsWith` and `endsWith` say yes or no, and `indexOf` says where.

## Extracting Text

**`substring(start, end)`** = the characters from `start` up to (not including) `end`.

### Accessing Characters

`text[i]` gives the character at index `i`, as a one-letter String.

**Simple lesson:** `substring` cuts out a piece by index, and `[i]` reads one character.

## Modifying Strings

Strings never change. These methods give back a **new** string:

| Method | What it gives |
|---|---|
| `replaceAll(a, b)` | every `a` changed to `b` |
| `replaceFirst(a, b)` | only the first `a` changed |
| `replaceRange(start, end, x)` | the part from `start` to `end` replaced by `x` |
| `padLeft(n, c)` | adds `c` on the left until the length is `n` |
| `padRight(n, c)` | adds `c` on the right until the length is `n` |

```dart runnable title="Replacing and padding"
void main() {
  var notice = 'Holiday on Monday. Monday is a holiday.';
  print(notice.replaceAll('Monday', 'Friday'));
  print(notice.replaceFirst('Monday', 'Friday'));
  print('9876543210'.replaceRange(2, 8, '******'));
  print('7'.padLeft(3, '0'));
  print('Maths'.padRight(10, '.') + '92');
  print('Science'.padRight(10, '.') + '85');
}
```

`padLeft(3, '0')` turns roll number `7` into `007`. `padRight` lines up a report card.

**Simple lesson:** `replaceAll` swaps text, and `padLeft` and `padRight` make text a fixed width.

## Case Conversion and Whitespace

- **`toUpperCase()`** / **`toLowerCase()`** = all capitals, or all small letters.
- **`trim()`** = removes spaces at both ends.
- **`trimLeft()`** / **`trimRight()`** = only at the left, or only at the right.

```dart runnable title="Cleaning up a typed name"
void main() {
  var typed = '   aSHa   ';
  print('[${typed.trim()}]');
  print('[${typed.trimLeft()}]');
  print('[${typed.trimRight()}]');
  var clean = typed.trim().toLowerCase();
  print(clean);
  print(clean[0].toUpperCase() + clean.substring(1));
}
```

The brackets show where the spaces are. The last line makes "Asha" with a capital first letter.

**Simple lesson:** `trim()` removes extra spaces, and the case methods fix capital letters.

## Splitting and Joining

- **`split(separator)`** = cuts a string into a **List** of pieces.
- **`join(separator)`** = the opposite, on a list: glues the pieces into one string.

```dart runnable title="From text to a list, and back"
void main() {
  var csv = 'Asha,Ravi,Priya,Kiran';
  var names = csv.split(',');
  print(names);
  print(names.length);
  print(names.join(' | '));
  print('Hello'.split(''));
  var words = 'Dart is fun'.split(' ');
  print(words.reversed.join(' '));
}
```

`split('')` cuts between every character.

### String Conversion

**`toString()`** turns any value into text (see **Data Types**): `92.toString()` gives `'92'`.

**Simple lesson:** `split` turns text into a list, and `join` turns a list into text.

## StringBuffer

### StringBuffer Fundamentals

**StringBuffer** = a tool for building a long string piece by piece.

Joining with `+` in a loop makes a brand new string every round. A StringBuffer keeps adding to one place, which is faster.

### StringBuffer Operations

| Method | What it does |
|---|---|
| `write(x)` | adds `x` |
| `writeln(x)` | adds `x` and a new line |
| `writeAll(list, sep)` | adds every item, with `sep` between |
| `clear()` | empties the buffer |
| `toString()` | gives the finished string |

```dart runnable title="Building a report card"
void main() {
  var marks = {'Maths': 92, 'Science': 85, 'English': 78};
  var report = StringBuffer();
  report.writeln('REPORT CARD');
  report.writeln('-----------');
  marks.forEach((subject, mark) {
    report.write(subject.padRight(10));
    report.writeln(mark);
  });
  report.write('Subjects: ');
  report.writeAll(marks.keys, ', ');
  print(report.toString());
  print('Characters: ${report.length}');
}
```

**Simple lesson:** use a StringBuffer to build long text, then call `toString()` once at the end.

## Runes and Unicode

### Unicode Characters and Emoji

**Unicode** = a world-wide list that gives every character a number: `A` is 65, `अ` is 2309, `😀` is 128512.

- Dart strings store text as **UTF-16** units.
- Most characters use **one** unit.
- Many emoji use **two** units, so `length` counts them as 2.

### runes

**Rune** = one Unicode character code.

```dart runnable title="Emoji, length and runes"
void main() {
  var smile = '😀';
  print(smile.length);
  print(smile.runes.length);
  print(smile.runes.first);
  print('नमस्ते'.length);
  print(String.fromCharCode(65));
  print(String.fromCharCodes([72, 105]));
  print('\u{1F44D} Well done!');
}
```

- `runes` counts real characters, so `😀` is 1 rune.
- **`String.fromCharCode(n)`** = turns a character code into a string.
- **`\u{...}`** = writes a character by its Unicode number.

Never cut a string with `substring` in the middle of an emoji. Half an emoji shows up as a broken box.

For text with emoji, the **characters** package handles real letters safely (see **Libraries**).

**Easy way to remember**

- **Find:** `contains`, `indexOf`. **Cut:** `substring`, `split`.
- **Change:** `replaceAll`, `toUpperCase`, `trim`, `padLeft`.
- **Build:** interpolation for short text, StringBuffer for long text.

**Simple lesson:** `length` counts UTF-16 units, and `runes` counts real characters.

## TL;DR

1. **String** = text in quotes. Multi-line: `'''...'''`. Raw: `r'...'`.
   - Never changes. Methods give a new string.
2. **Interpolation:** `$name` and `${expression}`. **Join:** `+`, or adjacent literals.
3. **Compare:** `==` (exact), `compareTo` (`-1`, `0`, `1`).
   - **Search:** `contains`, `startsWith`, `endsWith`, `indexOf`, `lastIndexOf`.
4. **Cut and change:** `substring`, `[i]`, `replaceAll`, `replaceFirst`, `replaceRange`, `padLeft`, `padRight`.
   - **Clean:** `toUpperCase`, `toLowerCase`, `trim`. **Split and join:** `split`, `join`.
5. **StringBuffer** = `write`, `writeln`, `writeAll`, `clear`, `toString`.
   - **Unicode:** `runes`, `codeUnits`, `String.fromCharCode`, `\u{...}`.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `'$name.length'` | `$` takes the whole expression | Use `${name.length}` | It prints the name, then `.length`. |
| `name.toUpperCase();` alone | It changes `name` | Strings never change | Nothing happens. Store the result. |
| `'Total: ' + 92` | `+` joins anything | `+` needs two Strings | A type error. |
| `substring(1, 3)` gives 3 characters | The end index is included | The end is NOT included | One character too few. |
| `'Asha' == 'asha'` is `true` | Case does not matter | `==` is exact | `false`. Compare lower case versions. |
| Building text with `+` in a long loop | It is just as fast | It makes many copies | Slow code. Use a StringBuffer. |
| Counting emoji with `length` | One emoji = 1 | Many emoji are 2 UTF-16 units | Wrong counts. Use `runes`. |

## Check Yourself

## Exercises

## What to Remember

- A string is text in quotes. It never changes, so every method gives a new string.
- Use `$name` and `${expression}` to put values inside text.
- `contains`, `indexOf` and `substring` find and cut text.
- `replaceAll`, `trim`, `toUpperCase`, `split` and `join` change and reshape text.
- Build long text with StringBuffer, and use `runes` to count real characters.

Next: **Date and Time**. You will learn to work with dates, times and durations, like exam days and homework deadlines.
