Skip to content
OneKitly

Cleaning Up a List Pasted from a Spreadsheet or a PDF

Published 8/6/2026 · 12 min read · Text & language tools

Daniel Okonkwo

Daniel OkonkwoFront-end developer and tech writer at OneKitly

Web performance · File formats

Checked against 4 sources

View profile
In short

Run trim-lines first, then remove-blank-lines. That order matters because remove-blank-lines decides what is blank with JavaScript's .trim(), which does treat U+00A0 (no-break space) as whitespace, but does not touch the text of the lines it keeps — so a line that reads "Lyon " survives with its three trailing spaces intact. trim-lines uses the same .trim() and cleans both ends of every line, including a stray carriage return. Between them they handle spaces, tabs, no-break spaces, thin spaces and the byte-order mark. Two characters escape both: U+00AD, the soft hyphen a PDF inserts where it broke a word across two lines, and U+200B, the zero-width space. Neither is whitespace in JavaScript, so they pass through remove-blank-lines, trim-lines, trailing-whitespace-remover and remove-whitespace unchanged. Watch trailing-whitespace-remover in particular: it matches only ASCII space and tab, so on the string "x" followed by a no-break space it changes nothing at all, and on "x" + no-break space + ordinary space it removes the ordinary space and leaves the no-break one. All four tools also silently rewrite CRLF line endings to plain line feeds, which is usually what you wanted, and none of them treats a lone carriage return as a line break at all. A spreadsheet paste additionally brings tab characters between the cells of a row — remove-whitespace with its default settings will delete those tabs and glue your columns together.

A paste carries characters you cannot see: no-break spaces, soft hyphens, zero-width spaces, tabs and CRLF. Four cleanup tools were run against each of them, and they use three different definitions of whitespace.

What actually lands in the clipboard

The text you see on screen and the text in the clipboard are not the same object. A PDF has no lines and no paragraphs: it has boxes of glyphs at coordinates. When you select a column of text and copy it, the reader reconstructs a plausible reading order and inserts a line break at the end of every line box — so a sentence that ran across three printed lines arrives as three lines. If the typesetter hyphenated a word at one of those breaks, some producers write a real hyphen and some write U+00AD, the soft hyphen, which renders as a hyphen only when a break happens there and as nothing at all otherwise. And because most European PDFs are typeset with no-break spaces before colons, semicolons and units, a French or Italian document hands you U+00A0 in places you would swear were ordinary spaces.

A spreadsheet paste is tidier but not clean. Excel, Numbers and Google Sheets all put a tab between the cells of a row and a line break between rows, and on Windows that line break is CRLF — a carriage return followed by a line feed, two characters where you see one. Delete a block of rows in the middle of a selection and you often copy the empty rows too, which arrive as lines containing nothing but tabs. That is the shape the tools on this page are built for: a list where the useful lines are separated by lines that contain only invisible characters, and where some useful lines carry invisible characters of their own at one end or both.

Four tools, three definitions of whitespace

remove-blank-lines and trim-lines both call JavaScript's .trim(). The language defines what that removes as WhiteSpace plus LineTerminator, and WhiteSpace includes every character in the Unicode Space_Separator category — which is where U+00A0 lives, along with U+2009 (thin space) and U+202F (narrow no-break space) — plus the byte-order mark U+FEFF. Run each of those on a line of its own through remove-blank-lines and the line disappears. remove-whitespace uses the regular-expression class \s, which resolves to the same set. So three of the four tools agree with each other about the no-break space, and agree with what a reader would expect.

trailing-whitespace-remover does not. Its pattern is a run of ASCII space or tab anchored to the end of the line, and nothing else qualifies. Fed the string x followed by one no-break space, it returns the string unchanged. Fed x followed by a no-break space and then an ordinary space, it removes the ordinary space and returns x plus the no-break space — visibly shorter by nothing, and still not equal to x. This is not a cosmetic difference. If you are cleaning a list before deduplicating it, that surviving no-break space is what keeps two identical city names apart.

The two characters nothing here removes

U+00AD, the soft hyphen, and U+200B, the zero-width space, are not whitespace in JavaScript. The regular-expression class \s does not match them and .trim() does not remove them, which was checked directly rather than assumed. So a line containing nothing but a soft hyphen survives remove-blank-lines and looks, in the output box, exactly like an empty line that the tool refused to delete. The word co-operatives copied out of a hyphenating PDF, with a soft hyphen sitting between co and operatives, comes back identical from all four tools — and will never match the string co-operatives, or cooperatives, in any search you run afterwards.

There is one tool on this site that does delete them, and it is a bad trade for five of our six languages. remove-non-ascii strips every character above U+007F, which does take out the soft hyphen, the zero-width space, the no-break space and the byte-order mark. It also takes out every accented letter outright rather than folding it: the phrase Les cooperatives regionales, run with a soft hyphen inside the first word, came back as Les coopratives rgionales — the accented e simply gone, not replaced by an unaccented one. The honest route is find-and-replace with the offending character pasted into the Find box, which works because that tool escapes its search term and matches it literally. You have to obtain a copy of an invisible character to do that, which is exactly as awkward as it sounds.

Line endings, and the one break these tools do not see

All four tools split on a line feed with an optional carriage return in front of it, then join the result back with plain line feeds. That means every one of them normalises Windows CRLF endings to Unix line feeds as a side effect, whatever else you asked them to do — paste a spreadsheet block, run remove-blank-lines, and the output is shorter by one byte per row even if no line was removed. That is almost always what you wanted, but it is worth knowing it happened, because a file that has to go back into a Windows tool afterwards may need the endings put back.

The break they do not see is a lone carriage return with no line feed after it — the line ending classic Mac OS used until 2001, and the one a few older export routines and some database dumps still emit. Given the three characters a, carriage return, carriage return, b, all four tools treat the whole thing as a single line and return it untouched. Nothing warns you. If a paste comes back as one enormous line with no visible breaks and no error, that is the first thing to suspect, and the fix is to open the file in an editor that can convert line endings before you bring it here.

The order to run them in

trim-lines first, remove-blank-lines second. Running them the other way round gives the same result on most inputs, because remove-blank-lines already tests each line with .trim() before deciding — a line of three spaces is dropped whether or not you trimmed first. It was checked on the messy input two spaces, alpha, two spaces, then a tab-only line, then two spaces, beta, one space, then an empty line, then alpha: both orders produced alpha, beta, alpha. The reason to put trim-lines first anyway is what happens next. Feed that same input to duplicate-line-finder without trimming and it reports no duplicates at all, because the first alpha carries two leading and two trailing spaces. Feed it the trimmed version and it reports alpha.

Two things not to do. Do not reach for remove-whitespace on a spreadsheet paste unless you have decided the columns can go: with its default setting it deletes every space, tab and line break, and the header row Nom, tab, Ville, tab, CA came back as NomVilleCA. Turning on keep line breaks preserves the rows but still eats the tabs, so the columns are still glued. And do not run trailing-whitespace-remover expecting it to make two lines comparable — it removes ASCII space and tab and nothing else, which was the whole point of the second section. When the aim is comparison rather than tidiness, trim-lines is the one that closes the gap.

One line of input, run through each of the four tools, with the output they actually produced
InputWhat happensWhy
A line of three ordinary spaces, in remove-blank-linesRemovedThe test is line.trim().length > 0, and .trim() empties it
A line containing only U+00A0, in remove-blank-linesRemovedECMAScript WhiteSpace covers the whole Space_Separator category
A line containing only U+00AD, in remove-blank-linesKept, and looks empty in the outputThe soft hyphen is Cf, a format character, not whitespace
x followed by one U+00A0, in trailing-whitespace-removerReturned unchangedIts pattern is a run of ASCII space or tab at end of line, nothing wider
x, then U+00A0, then an ordinary space, in trailing-whitespace-removerComes back as x followed by U+00A0The run of ASCII space stops at the first character outside the class
The same x plus U+00A0, in trim-linesComes back as xSame .trim() as remove-blank-lines, applied to the text instead of the test
The header row Nom, tab, Ville, tab, CA, in remove-whitespaceNomVilleCA — the columns are goneA tab is whitespace, and keep line breaks only spares the line feed
a, carriage return, carriage return, b — in any of the fourTreated as one line, returned untouchedThey all split on a line feed with an optional carriage return before it
Remove blank linesDelete all empty or whitespace-only lines from your text.Try the tool

Frequently asked questions

Why does my list still have blank-looking lines after remove-blank-lines?
Almost certainly a zero-width space (U+200B) or a soft hyphen (U+00AD) sitting alone on the line. Neither is whitespace to JavaScript, so the tool's test — does this line have anything left after .trim() — answers yes, and the line stays. Both were run through the tool to confirm it. A no-break space or a thin space on its own would have been removed, so the surviving line is not one of those. The way to identify which character it is, without guessing, is to paste the line into a character or byte counter and see that the count is one rather than zero.
Is trailing-whitespace-remover broken, then?
It does what its own description says — remove trailing spaces and tabs — and that narrow definition is the right one for its usual job, which is stripping the invisible junk at the ends of lines of source code before a commit. Diff tools and linters care about ASCII space and tab; a no-break space in code is a bug you want to see, not one you want silently deleted. The problem is only that the name reads as a general promise. For prose and for lists pasted out of documents, trim-lines is the tool with the wider definition, and it is the one to use when you need two lines to compare equal afterwards.
How do I get rid of the soft hyphens a PDF put in my text?
None of the four tools in this article will do it. find-and-replace will, if you can get a soft hyphen into its Find box — the tool escapes whatever you type and matches it literally, which was verified, so pasting the character works and typing a description of it does not. Getting a copy of an invisible character usually means selecting a suspiciously wide gap in the source text with the arrow keys and shift, then copying that. remove-non-ascii also removes them, but it removes every accented letter with them and does not replace them, so for French, Spanish, Portuguese, German or Italian text it destroys more than it fixes.
Does any of this send my list to a server?
No. All four transforms are ordinary string operations that run in the page you have open, on the text in the box, and produce their output in the same page. Nothing about the shape of the work needs a server: splitting on line breaks and testing each line is a few lines of code, and there is no dictionary or model to consult. That matters more than usual here, because the lists people clean this way are often extracted from internal documents — client names, order references, staff rosters — and pasting one into a tool that uploads it is a data transfer decision, not a formatting one.
My spreadsheet paste keeps its tabs. How do I turn it into one item per line?
None of the four does that, and remove-whitespace in particular does the opposite — it deletes the tabs and welds the cells together, which was checked on a three-column header row that came back as one word. If you only ever wanted one column, copy one column out of the spreadsheet: the paste then contains no tabs at all and remove-blank-lines plus trim-lines is the whole job. If you have several columns and want them stacked one per line, that is a different operation with its own tool, and the honest answer is that these four are line-level cleaners, not column splitters.

Articles you may find interesting

All guides
ExplainerFinding Duplicates in a List Without a SpreadsheetTwo lines that look identical are often not identical. Case, a trailing space, a no-break space and two different encodings of the same accented letter were each run through the duplicate finder, and it reported no duplicates for three of the four.How-toFiltering Lines by a Pattern Without a Command LineThis is grep for people who do not use grep, with one important difference: the match is a plain substring, so a real regular expression returns an empty box and no error. Every claim here was checked by running the tool.How-toCleaning Messy Text: The Order of Operations That Actually MattersStrip tags before decoding entities, trim before deduplicating, collapse whitespace last. Three orderings run in Node, a nine-step pipeline in the right sequence, and the invisible characters — U+00A0, U+200B, U+FEFF — that survive every naive cleanup.GuideConverting Between List Formats Without Losing Data: The Quoting Rules Nobody ReadsTurning a newline list into a comma list is trivial until an item contains a comma. RFC 4180's quoting rules, why a CSV field may contain a newline, why European spreadsheets use the semicolon, and what an empty item does to a round trip — every case run and printed.ExplainerDetecting the Language of a Text, and Why Short Texts FailMeasured, not asserted: 90 short real phrases across six languages, none declined and 68 right — 76%, falling to 64% under sixteen letters. Four of the wrong answers came back at 100% confidence.How-toNumbering the Lines of a Text for a Review with Several PeopleNumbering starts at 1 and cannot be set to 0, the alignment is spaces rather than zeros, and the remover undoes eight of the eleven separators without touching the indentation. What it still cannot do is tell your numbers from its own.

Related tools

Everything here describes what these tools do today, checked by running their own transforms against the exact inputs printed in each article, not what a standard obliges a text tool to do. Line-level text handling has no single authority: what counts as whitespace, whether two accented lines are the same line, and where a URL ends in running prose are decided differently by every program you will ever paste into. Where a tool gets a case wrong, that is said plainly rather than worked around. Before you run any of this over a list you cannot re-export, run it over a copy and compare the line count at both ends.

Sources

Spotted a mistake in this article?