# Uri and WebUtility escaping

> Percent-encodes a string for a URL, and escapes a string for safe insertion into HTML. Escaping is exact and identical in the browser and on the server — including the characters that a browser's own encodeURIComponent would leave alone.

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

## Summary        {#summary}
`Uri.EscapeDataString` percent-encodes a string so it can be dropped safely into a URL. `WebUtility.HtmlEncode`
escapes a string so it can be dropped safely into HTML text. Both have inverses,
`Uri.UnescapeDataString` and `WebUtility.HtmlDecode`.

## Signature      {#signature}
```osy syntax
Uri.EscapeDataString(<string> s) -> string
Uri.UnescapeDataString(<string> s) -> string
WebUtility.HtmlEncode(<string> s) -> string
WebUtility.HtmlDecode(<string> s) -> string
```

## Description    {#description}

### URL escaping   {#url}

`Uri.EscapeDataString` keeps only the RFC 3986 *unreserved* characters — letters, digits, and `- . _ ~` — and
percent-encodes everything else as UTF-8 bytes:

```osy title="URL escaping — unreserved kept, everything else percent-encoded" syntax
Uri.EscapeDataString("hello world")   // "hello%20world"
Uri.EscapeDataString("a&b=c")         // "a%26b%3Dc"
Uri.EscapeDataString("café")          // "caf%C3%A9"
```

That includes `!`, `'`, `(`, `)` and `*`, which some URL encoders leave alone. Escaping the same string always
produces the same output, wherever the code runs.

`Uri.UnescapeDataString` reverses it, and is **forgiving**: a malformed escape is left exactly as written rather than
raising. `Uri.UnescapeDataString("%zz")` is `"%zz"`, and a `+` is a literal plus, not a space.

### HTML escaping   {#html}

`WebUtility.HtmlEncode` escapes the five characters that can break out of HTML text — `"`, `&`, `'`, `<`, `>` — and
renders every non-ASCII character as a numeric entity:

```osy title="HTML escaping — the five characters that break out of text" syntax
WebUtility.HtmlEncode("<script>alert('x')</script>")
// "&lt;script&gt;alert(&#39;x&#39;)&lt;/script&gt;"
```

Note that it does **not** escape `+`, `/`, `?` or `#` — they are harmless in HTML text. It escapes for **text**, not
for an attribute value or a URL; do not use it to build a `href`, and do not use it as a substitute for the platform's
own output escaping, which already applies wherever a value is rendered.

## Examples       {#examples}
```osy title="build a search link" test app=text-search
string SearchUrl(string term) {
  return "/search?q=" + Uri.EscapeDataString(term);
}
// SearchUrl("blue & green")  ->  "/search?q=blue%20%26%20green"
```

## See also       {#see-also}
- [Regex](https://osysharp.com/reference/stdlib/regex/) — pattern matching, which is likewise identical on both sides
- [execution side](https://osysharp.com/reference/function/execution-side/) — why escaping runs in the browser, with no round trip
