# char

> A single character, written in single quotes. It is what you get from `s[0]` and from iterating a string, and it is the argument the `char.*` classification family takes — `char.IsDigit`, `char.IsLetter`, `char.ToUpper`.

<!-- id: types-char · area: types · stability: stable · html: https://osysharp.com/reference/types/char/ -->

## Summary        {#summary}

A single character. Write one in **single quotes** — `'A'`, `'\n'`, `'7'` — the way C# does, and reach for it whenever
you are looking at text one character at a time: scanning a code, validating a format, splitting on a delimiter.

## Signature      {#signature}

```osy syntax
char Initial = 'A';
char Delimiter = ',';
char Tab = '\t';
```

## Description    {#description}

A `char` is one character, not a one-character string, and the distinction earns its keep in two places.

**It is what indexing and iteration give you.** `s[0]` is the first character of `s`; `foreach (var c in name)` walks
them in order. Both read as a `char`, so the classification family below applies directly without a conversion step.

**It picks the overload.** `s.Split(',')` splits on one character; `s.Split(", ")` splits on a two-character
sequence. Those are different operations, they are different overloads in C#, and they stay different here.

### Where it is stored     {#storage}

On an entity, a `char` member is a one-character column. The width is the point: a column that could hold two
characters would let a row exist that the type says cannot.

### Ordering        {#ordering}

Characters compare by code point, so a range test reads the way you would write it in C#:

```osy title="a range test on a character" syntax
c >= 'a' && c <= 'z'
```

### Classification  {#classification}

`char.IsDigit`, `char.IsLetter`, `char.IsLetterOrDigit`, `char.IsWhiteSpace`, `char.IsUpper`, `char.IsLower` and
`char.IsPunctuation` each ask about a single character. `char.ToUpper` and `char.ToLower` hand back a `char`;
`char.Parse` turns a one-character string into one, and **refuses** a string of any other length rather than taking
its first character — `s[0]` is how you say that.

Each of these answers by **Unicode category**, not by an ASCII range. `char.IsDigit('٠')` is true — that is an
Arabic-Indic zero — and `char.IsDigit('²')` is false, because a superscript two is a number but not a digit. The
answer is the same in the browser and on the server.

### One character means one UTF-16 unit    {#code-units}

A `char` holds a single UTF-16 code unit, exactly as in C#. Characters outside the Basic Multilingual Plane — most
emoji, some historic scripts — are **two** units, so they cannot be held in a `char`, and indexing into a string
containing one will land on half of it. When you are handling arbitrary user text rather than a code or a delimiter,
work with the `string` and its own operations instead.

## Examples       {#examples}

```osy title="scanning a string one character at a time" test app=char-basics
int CountDigits(string s) {
  int n = 0;
  foreach (var c in s) {
    if (char.IsDigit(c)) { n = n + 1; }
  }
  return n;
}
```

```osy title="the first character, as a character" test app=char-basics
char Initial(string name) {
  return name[0];
}
```

```osy title="a one-character member on an entity" test app=char-basics
entity Grade {
  [Required, MaxLength(80)] string Subject;
  char Letter;
  security { allow read, create when IsAuthenticated; }
}
```

```osy title="splitting on one character, and on a sequence" test app=char-basics
string FirstField(string row) {
  return row.Split(',')[0];
}
```

### The characters as an array — `ToCharArray`   {#tochararray}
`foreach (var ch in text)` is the usual way to walk a string, and it is what most code wants. (It is the `Text`
module underneath, as every string verb is — you write the fluent form.) When you need the
characters as a value you can index, count or pass on, `text.ToCharArray()` hands them back as a `char[]` — C#'s own
spelling, doing C#'s own thing:

```osy title="when you need them as a value rather than a loop" syntax
char[] letters = code.ToCharArray();
int n = letters.Length;
```

## See also       {#see-also}
- [string literals — ordinary, verbatim and raw](https://osysharp.com/reference/types/string-literals/) — the other quoting form, and the escapes both share
- [Text.Length, Text.IsEmpty, Text.IsBlank, Text.Contains, Text.StartsWith, Text.EndsWith](https://osysharp.com/reference/function/text-inspect/) — asking a string a question: contains, starts with, index of
