# Text.Capitalize, Text.Replace, Text.Repeat, Text.Left, Text.Right

> Produce a new string from an old one: upper-case the first letter, replace every occurrence of a substring, repeat it, or take characters from one end. Replace hits every non-overlapping occurrence left-to-right and throws on an empty search; Left/Right clamp rather than throw. All run in memory.

<!-- id: function-text-transform · area: function · stability: stable · html: https://osysharp.com/reference/function/text-transform/ -->

## Summary        {#summary}
Each of these returns a **new** string. `Text.Capitalize(s)` upper-cases the first character and leaves the
rest alone. `Text.Replace(s, old, new)` swaps every occurrence of `old` for `new`. `Text.Repeat(s, n)`
concatenates `s` with itself `n` times. `Text.Left(s, n)` and `Text.Right(s, n)` take `n` characters from
the start or end.

## Signature      {#signature}
```osy syntax
Text.Capitalize(<string> s) -> string             // first character upper, rest unchanged
Text.Replace(<string> s, <string> old, <string> new) -> string   // EVERY occurrence
Text.Repeat(<string> s, <int> n) -> string
Text.Left(<string> s, <int> n)  -> string         // first n characters
Text.Right(<string> s, <int> n) -> string         // last n characters
```

## Description    {#description}

### Capitalize touches only the first character   {#capitalize}
`Text.Capitalize` upper-cases the **first** character and copies the rest verbatim — it does **not**
lower-case the tail, and it does **not** touch other words. `Text.Capitalize("hELLO wORLD")` is
`"HELLO wORLD"`. If you want each word title-cased with its tail lowered, that is a different function —
[Text.TitleCase](https://osysharp.com/reference/function/text-titlecase/). The casing is invariant simple-case mapping, so a ligature like `ﬁ` is left as
it is rather than expanded.

### Replace hits EVERY occurrence, left-to-right, non-overlapping   {#replace}
`Text.Replace("a-b-c", "-", "+")` is `"a+b+c"`. Matching is greedy and left-to-right with no overlap, so
`Text.Replace("aaa", "aa", "b")` is `"ba"` — the first `"aa"` is replaced, leaving a trailing `"a"`. The
search is a **literal**, not a pattern: `Text.Replace("a.b", ".", "-")` replaces the actual dot. An **empty**
`old` **throws** (there is nothing to find) — guard it if the search text is user-supplied.

### Repeat and Left/Right clamp, they do not throw   {#clamping}
`Text.Repeat(s, 0)` and a negative count both yield `""`. `Text.Left`/`Text.Right` **clamp**: asking for
more characters than the string has returns the whole string, and a negative count returns `""` — neither
throws. (This is unlike `Text.Substring`, which throws when its range runs past the end.)

All of these run **in memory** on a value already in hand.

## Examples       {#examples}
```osy title="build a short, tidy label" test app=text-transform
// Capitalise, then keep it short with an ellipsis if it overruns.
string ShortLabel(string raw) {
  var c = Text.Capitalize(raw);
  if (Text.Length(c) <= 8) { return c; }
  return Text.Left(c, 7) + "…";
}
```

```osy title="the exact answers, pinned" run app=text-transform
[Test]
void Text_transform_answers() {
  Assert.Equal("Hello", ShortLabel("hello"));
  Assert.Equal("Announc…", ShortLabel("announcement"));   // clamped Left + ellipsis

  Assert.Equal("HELLO wORLD", Text.Capitalize("hELLO wORLD"));   // only the first char
  Assert.Equal("a+b+c", Text.Replace("a-b-c", "-", "+"));
  Assert.Equal("ba", Text.Replace("aaa", "aa", "b"));            // greedy, non-overlapping
  Assert.Equal("a-b", Text.Replace("a.b", ".", "-"));            // the dot is literal

  Assert.Equal("ababab", Text.Repeat("ab", 3));
  Assert.Equal("", Text.Repeat("ab", -2));                       // clamps to empty

  Assert.Equal("He", Text.Left("Hello", 2));
  Assert.Equal("lo", Text.Right("Hello", 2));
  Assert.Equal("Hello", Text.Left("Hello", 99));                 // clamps, never throws
  Assert.Equal("", Text.Right("Hello", -1));
}
```

## See also       {#see-also}
- [Text.TitleCase](https://osysharp.com/reference/function/text-titlecase/) — capitalise EACH word (and lower-case the tails), unlike `Capitalize`
- [Text.LastIndexOf](https://osysharp.com/reference/function/text-lastindexof/) — `Text.Substring`, which THROWS out of range where `Left`/`Right` clamp
- [Text.Length, Text.IsEmpty, Text.IsBlank, Text.Contains, Text.StartsWith, Text.EndsWith](https://osysharp.com/reference/function/text-inspect/) — `Text.Length`, `Text.Contains`, and the emptiness checks
