Cleaning Text

How to remove control characters on Mac

You can do this with Note My. Here is how to see exactly which non-printable characters a log or export is carrying, where they are, and how to remove them.

Something in the file is wrong and you cannot see it. A parser stops on line one. A diff claims two identical lines differ. A terminal capture pastes into a document and drags a trail of odd boxes behind it. The usual cause is a set of characters that were never meant to be printed at all: NUL bytes, terminal escape codes, form feeds, and the rest of the ASCII control range.

This guide covers what counts as a control character in Note My, how to find every one in a document, and how to remove them without losing the tabs and line breaks that hold the file together.

Note

All of this happens on your Mac. Nothing is uploaded, there is no account to create, and a production log you open and clean never leaves the machine.

Which characters count, and which are left alone

Control characters are one of the categories our hidden-character scanner covers, and the list of what counts is exact:

The code points treated as control characters, and the three that are excluded
RangeWhat lives there
U+0000–U+0008NUL, the bell, backspace and the early C0 codes
U+000B, U+000CVertical tab and form feed
U+000E–U+001FThe rest of C0, including U+001B ESC — the first byte of every terminal colour code
U+007FDEL
U+0080–U+009FThe C1 range
ExcludedTab, line feed and carriage return are not in the category and are never touched by it

That exclusion is the important half. Tab, LF and CR are technically control characters too, and an editor that removed them along with the rest would flatten your file into one line and destroy your indentation. They are deliberately left out.

A control character is never a judgement call. It is safe to remove anywhere — in code, in a string literal, in a comment, in prose — and it is deleted rather than swapped for something else. That is also why the Protect authored text setting, which keeps your strings and comments from being tidied up, makes no difference here. A NUL byte inside a string literal is still a NUL byte, and it goes.

Where they come from

Three sources cover almost every case. Terminal captures carry U+001B escape sequences from any tool that colours its own output — on screen that is colour, in a file it is junk that breaks every regex you write against it. Database and system exports leave NUL bytes at U+0000 where a binary column or a padded fixed-width record was written out as text; these are the ones that make an editor refuse the file outright. Files decoded with the wrong encoding turn high bytes into C1 control characters. If a file is full of C1 characters, the fix is an encoding fix rather than a cleaning one — see Troubleshooting below.

Opening a file macOS refuses to call text

A file with NUL bytes in it often will not open by double-clicking, because macOS does not offer it to you as a text file. Press ⇧⌘O for File ▸ Open Anything as Text… and pick it. The file opens as text, control characters and all, so you can see what it is carrying. Note My will not make itself the default application for files like these: it opens them when you ask, and leaves your Mac's file associations alone.

If the contents look binary you get a warning first, and that warning is on by default. Heed it: cleaning control characters out of an actual binary file will corrupt it.

Find and remove them

  1. Check the status bar first

    The hidden-character pill is the quick signal. It stays hidden when there is nothing to say, shows Clean in green, "N hidden" in orange, or "N hidden · D risky" in red. Click it for Show in Text, Remove Invisible Character(s), Review All… and Go to Next, plus switches for Protect Strings & Comments and Check While Typing.

    Treat it as your live signal while you work. It flags the characters that are never legitimate, control characters among them, so a capture you have only just opened can tell you where it stands before you have read a line of it. How much of a document it keeps watch over is yours to set: raise the Scan documents up to slider in Settings ▸ Text Cleanup and it goes on reporting across progressively bigger captures, lower it and it stays quiet on the long ones. When you want the complete account of a file rather than a running signal, open the sheet in the next step — that is the job it was built for.

  2. Open the Review All sheet

    Choose Clean ▸ Clean Invisible / Special Characters… from the toolbar's Clean menu, or Review All… in the pill's popover. This sheet is the authoritative pass: it covers every category, and it works through the whole document however long it is. Our large-file engine keeps a long capture open and responsive, so a big log is not a reason to split the file up first. It is worth reaching for the sheet whenever the file matters — a database export you are about to reimport, a log you are about to attach to a ticket — because it shows you the full picture before anything changes.

  3. Set the scope and the preset

    The header has a segmented Selection | Whole Document control and a preset pop-up. For this job choose Safe. It is seven categories: zero-width characters, bidi control characters, soft hyphens, control characters, line separator, paragraph separator and variation selectors. Nothing about your spacing, your quotes or your line endings is in scope. Prose is a shorter list still, six categories, but it adds trailing whitespace — so Safe is the one that touches nothing but the invisibles.

    The default preset, Code-safe, covers sixteen categories. It removes control characters too, but also normalises unusual spaces, smart punctuation, trailing whitespace and lone carriage returns. If you want only the control characters gone, Safe is the one.

  4. Scan

    Press Scan. The left table lists the 21 categories in a fixed danger-first order, each with a checkbox, a severity chip and a found count. Untick everything except Control characters if you want to be surgical — note that touching any checkbox switches the preset pop-up to Custom, which is the sheet telling you honestly that you are no longer running a named preset.

  5. Read the findings

    The right-hand table lists every individual finding with five columns: Line:Col · Code Point · Category · Action · Risk. The code point is the answer to "what actually is this?" — U+0000, U+001B, U+0085. Double-click any row to reveal it in the editor, or select rows and press Show in Editor. Line numbers always refer to the whole document, even when you scanned a selection, so they match what the ruler shows.

  6. Apply

    The footer reads Apply (N). It acts on every finding whose category is ticked and whose Action column says it will be changed. The whole operation undoes as one action named "Clean Invisible Characters", so a single ⌘Z puts the document back exactly as it was. The counts update afterwards, so you can see at a glance what is left and close the sheet knowing where you stand.

The one-click route

For a handful of characters you do not need the sheet at all. The pill's popover has Remove Invisible Character(s), which fixes everything found in one undo group named for the count it removed. It acts on the characters that are never legitimate, which is exactly the group control characters belong to, so a terminal capture with a few stray escape codes in it is clean in a single click. Anything where context matters — straight quotes, unusual spaces and the rest — waits for you in the sheet, where you decide case by case rather than having it decided for you.

To step through them one at a time instead, the Clean menu carries Find Next Hidden Character and Find Previous Hidden Character, and the popover carries Go to Next. Each selects the character, scrolls to it and shows a find indicator, wraps at the end of the document, and beeps when nothing is left — so you can walk a whole log end to end and know when you have reached the last one. Both sit in the Clean menu, alongside the sheet and the removal rules, so the entire job is under one heading.

Seeing them while you work

Findings are also drawn in the editor as markers over the text. Control characters get the "slug" treatment — a visible block where an invisible thing sits — so a line that looked empty stops looking empty. Revealing a marker pulses it briefly and then settles; with Reduce Motion on, the pulse is skipped. Markers stay with the text until you edit it, so what is drawn on screen always matches the document in front of you; scan again after an edit for a fresh set.

Separately, the toolbar's Show Invisibles toggle changes how whitespace is drawn, with three marker styles: Symbols (the default), Code Points, and Bytes, which prints hex in the document's own encoding — a space is 20 in UTF-8 and 20 00 in UTF-16 LE.

Important

Show Invisibles changes drawing only. It never edits the document. Removing characters is always the Clean menu's job.

Stripping them in bulk with a rule

If the same source keeps producing the same junk, write it down once. Clean ▸ Edit Removal Rules… opens a window titled Residual Removal Rules, where you keep our cleanup rules: a list of regular-expression rules, each with a pattern, a replacement template (leave it empty to delete), and an enabled switch. Eleven built-ins ship with it, including zero-width characters, soft hyphen, word joiner and non-breaking space; built-ins are read-only, so you disable or duplicate them rather than editing them.

Add your own rule for the exact escape sequence your tool emits, use the Analyze panel to see how many matches it makes against the current document or selection, then run Clean ▸ Clean Now to sweep every enabled rule over the whole document as one undo group named "Clean Residuals". Clean Now goes straight to work, which is the point of it: the Analyze panel is where you satisfy yourself a rule matches what you think it matches, and the whole sweep steps back with one ⌘Z if you change your mind. Rules import and export as JSON, so a rule set you have refined for one noisy export can move to another Mac as a plain file.

Troubleshooting

Every other character is a C1 control character

That is not a cleaning problem, it is an encoding problem. The file was written in one encoding and opened as another, and cleaning the symptoms away would only lose you text you want. Use File ▸ Encoding ▸ Reload with Encoding… and pick the encoding the file was actually written in. Our encoding handling lets you try one and then another, and reloading changes nothing on disk, so a wrong guess costs you nothing. Convert to Encoding… is the opposite operation — it changes what gets written back out — so do not reach for it here.

I want certainty on a very long capture

Open the Review All sheet and scan the whole document. It works through the file from the first line to the last however long it runs, our large-file engine keeps the window responsive while it does, and the findings table gives you a count and a code point for every single one. That is the authoritative answer, and it is the one to reach for before a file goes anywhere else. Settings ▸ Text Cleanup is where you tune the status pill that watches over your shoulder the rest of the time.

Apply is greyed out

Apply lights up as soon as there is something for it to act on. Check that Control characters is ticked in the left table and that the scan found some. If the only findings listed are ones the sheet reports for information — emoji joiners and the like — Apply waits, which is the behaviour that keeps a clean-up from touching characters you meant to keep. Tick the category you want and scan again.

The findings came back but the characters are still there

Check the scope control. A scan run against Selection applies only to that selection, even though the line numbers are reported against the whole document. Switch to Whole Document and scan again.

I removed too much

One ⌘Z. Every cleaning operation undoes as one named action — "Clean Invisible Characters" from the sheet, "Clean Residuals" from Clean Now — so a sweep of a thousand characters reverses in a single step.

What else you can do with the file

Removing the control characters is usually the middle of the job rather than the end of it. Once the file is clean, the rest of the work is on the same menus.

  • Find out which files are affected before you open any of them. Search ▸ Find in Folder… (⇧⌘F) searches a whole directory tree and reports back without writing a byte, so you can point it at a folder of exports, see which ones carry escape codes, and open only those. Nothing in that tree changes until you open a file and ask for a change.
  • Work through several files in one sitting. Our find and replace runs across every open tab, so a set of captures from the same noisy tool can be dealt with together rather than one at a time.
  • Confirm that only the invisible characters went. Keep a copy of the export as it arrived, and our comparison view puts the two versions side by side, so before you hand a database file back you can see exactly what changed and satisfy yourself that every line of visible text survived the trip.
  • Read the log rather than stripping it. Turn on View ▸ Enable Log Highlighting and our syntax engine colours severity keywords, timestamps, HTTP status codes, IP addresses and URLs on screen, without writing a single character into the file. It is the readable version of what those escape sequences were trying to give you, and it survives the cleaning because it was never in the file to begin with.
  • Send it on in the encoding the next tool expects. File ▸ Encoding ▸ Convert to Encoding… writes the file back out as UTF-8, UTF-16 or whatever the receiving system wants, so a capture that arrived awkwardly leaves in good order.
  • Keep the long ones open. Our large-file engine holds a long capture in a responsive window, so scrolling, searching and a full pass through the Review All sheet behave the same way they do on a short file, and splitting the log up first is never part of the routine.

If a particular export keeps arriving in the same state, the removal rules above turn all of this into a few seconds' work the second time round: open the file, run Clean Now, save. The rule you wrote once carries the knowledge, so you never have to remember which escape sequence that tool emits.

Frequently asked questions

Will removing control characters break my tabs or line breaks?

Your indentation and line structure come through exactly as they were. Tab, line feed and carriage return are explicitly excluded from the category, so a cleaned file still opens in your editor looking like the file you started with. Lone carriage returns and mixed line endings are handled as separate categories, which the Safe preset leaves alone — you deal with those deliberately, when you want to, and not as a side effect of stripping escape codes.

What happens to emoji?

Emoji come through intact. Family emoji, skin-tone sequences and flags are built out of several characters held together by invisible joiners, and those joiners are reported to you for information rather than removed — you can see them listed with their code points, which is often interesting in itself, and they stay exactly where they are. Clean a chat export and every emoji in it arrives whole on the other side.

How does Protect authored text apply here?

Control characters go either way, so you can leave the setting exactly as you like it. That protection is there for the findings where context matters — smart quotes and dashes inside strings and comments, for example, which you almost certainly typed on purpose. A control character is never something anyone typed on purpose, so it is removed whether the setting is on or off, including from inside a string literal.

What is the difference between Clean Now and the Review All sheet?

Two different tools on one menu. Clean Now applies our cleanup rules — the regular-expression list you maintain yourself — immediately and without a preview. The Review All sheet uses our hidden-character scanner and shows you every finding, with its code point, before anything changes. Use the sheet when you want to look first, Clean Now when you already trust your rules.

Can I see what a character is without removing it?

Yes. The findings table's Code Point column names it, and double-clicking a row reveals it in the editor. Scanning never changes the document; only Apply does.

Can I have this happen automatically when I paste?

Yes, with a setting you have to turn on yourself. Clean pasted text in Settings ▸ Text Cleanup is off by default; turn it on and pasted text is cleaned with your default preset as it lands in the document.