Unicode Test String Generator
Pasting accented letters into a field proves it accepts accented letters. It does not find the bug. The bugs are specific and they have names: a username that trims to look identical to another one, a filename that displays as a PDF and runs as an executable, a column that accepts a name in one normal form and rejects the same name in the other, a truncation that slices an emoji in half. This builds one string for each of eight such failures, says which one it provokes and what a correct implementation does instead — and then measures the finished string to confirm it really has the property claimed for it.
What this generator does
Builds a string per failure mode rather than a sample of exotic characters. Each one carries the measurements that matter for its own mode — code points against UTF-16 units against UTF-8 bytes for the surrogate case, the two normal forms' byte lengths for normalisation, the logical and display orders for the bidi case — plus a sentence on what a correct implementation should do with it.
How to use this tool
- Choose one failure, or take one of every kind.
- Press generate. Each string comes with the numbers that make it dangerous.
- Copy the strings alone for a fixture file, or with the explanations for a ticket.
- Paste into the field, the filename, the column or the API you are testing.
- Set a seed if you want to rebuild the exact string that broke something.
Worked examples
- Zero-width character surviving a trim
- One draw returned the username “admin” — six code points, displaying as “admin”, unchanged by trim() because U+200B is not whitespace, and not equal to “admin”. A uniqueness check that passes it through holds two rows that look the same.
- Bidi override in a filename
- “accounts-fdp” is written f-d-p and displays as “accounts-pdf”. U+202E reverses the run and U+202C closes it, so nothing after it looks wrong.
- Normalisation changing the byte length
- “cafṩ” is 6 UTF-8 bytes in NFC and 8 in NFD. A VARCHAR counted in bytes accepts one form and rejects the other — the same name, typed on two machines.
Common use cases
- Building a Unicode fixture set for a test suite, with one case per named failure
- Checking that a username or email uniqueness rule strips zero-width characters
- Finding the length arithmetic that truncates an emoji into a lone surrogate
- Testing whether an upload path rejects reserved Windows device names
- Proving a search or login compares normalised text rather than raw bytes
- Showing a team, with a real example, why a bidi override in a filename matters
How this generator works
Each mode is a small producer with a property, and the property is checked against the finished string rather than against the code that built it. The surrogate case is re-measured to confirm UTF-16 units exceed code points and that slicing at the stated limit really does leave a lone high surrogate. The normalisation case re-normalises both ways and confirms the two forms differ and that their UTF-8 byte lengths differ. The zero-width case confirms trim() leaves the character in place and that the result is not equal to the plain word it displays as. The combining case confirms every code point after the base is a combining mark and that the whole thing is one grapheme. The bidi case confirms the override is present, is closed, and encloses a run that is not a palindrome — without which the display order would equal the logical order and the spoof would not happen. Separating the check from the producer earned its keep immediately: it rejected three entries in the banks, including a fullwidth A that decomposes only under NFK forms, and U+FEFF, which is the one invisible character in the set that a whitespace trim strips rather than leaves — so a string built from it to survive trimming does not.
Randomness and fairness
Which string you get within a mode is drawn with the browser's cryptographic random number generator, or from a seed if you set one, in which case the same seed rebuilds the same strings. A seed is there so a failing fixture can be rebuilt, not for secrecy: seeded strings are deterministic, not cryptographic. The failure each mode provokes is not random: that is the specification.
For how randomness is produced across the whole site, see how Generate Random works.
Limitations and good to know
- Eight failure modes, which is not all of them. Overlong UTF-8 encodings, lone surrogates inserted directly, Han unification and locale-dependent case folding (Turkish dotless i) are real classes and are not here.
- The strings are built to break code, not to be realistic. They are fixtures for a test suite, not sample data for a demo.
- Five modes draw from fixed banks and repeat — 10 surrogate strings, 9 numeric values, 54 bidi names, 78 normalisation words, 110 reserved filenames (twenty-two reserved Windows names, each with five endings) — because the value is covering the named cases, not variety. Zero-width has 144, and the other two vary widely; combining marks alone gave 400 distinct strings in 400 seeded draws.
- Several of these render as nothing, as a fallback box, or as a stack of marks over the line above, depending on your fonts and your browser. That is the point rather than a display fault, but it means a screenshot is a poor way to pass one on — copy the string.
- The reserved-filename and numeric modes are not Unicode problems at all. They are in the same set because they are what a tester actually needs beside the others, and separating them by cause rather than by use would help nobody.
- Pasting these somewhere real can genuinely break it: mis-sorted lists, mangled display, a truncation mid-character, a file that cannot be deleted. Use them against something you are willing to break.
Privacy and your data
Everything is built in your browser. No string is transmitted, stored or included in analytics; only the fact that the tool ran and how many strings it made are counted.
Related generators
- Random ASCII Test String GeneratorA random printable-ASCII test string, including symbols.
- Random Alphanumeric String GeneratorA random string of letters and digits only, no symbols.
- Text Encoder and DecoderEncodes text as Base64, percent-encoding, HTML entities, hex or binary, and back again — accents and emoji included.
- Locale Code PickerA real BCP 47 locale code, like en-GB or ja-JP, for testing internationalisation.
- Timezone Test Value GeneratorA real IANA timezone identifier for testing date/time handling across regions.
- Checksum and Hash CalculatorWorks out SHA-256, CRC-32, Adler-32 or FNV-1a for any text, checked against the published values.