Text Processing Cheat Sheet
Deduplicating, stripping blank lines, diffing, masking and counting words — these small text chores come up for nearly every developer and operator. This page tabulates nine common operations and disambiguates the five counting metrics people most often confuse.
How It Works
Text processing is a daily need: case conversion, whitespace collapsing, newline normalization (mixing Windows CRLF with Unix LF creates noisy diffs), encoding detection/transcoding (UTF-8 ↔ GBK), deduplication and counting.
It rests on character-encoding knowledge and regex. Beware invisible characters — BOM, zero-width spaces, non-breaking spaces — that silently break string comparison and hashing. Normalize input (e.g. NFC) before validating to avoid hidden bugs.
When to Use This Table
This table is for everyday text operations: search and replace, splitting and joining, case conversion, deduplication. It lists what each operation does and when to reach for it. Establish the input structure first — single line, multiple lines, or delimiter-separated records.
When not to consult it: you are building a full-text search engine or doing text mining. This page covers routine string and line operations, not indexing, tokenisation or semantic analysis.
Rule of thumb: look at the input structure. Operations within a single line (extracting emails, replacing characters) use different tools than cross-line ones (splitting by line, deduplicating and sorting); and structured text such as CSV or TSV must be parsed by delimiter rather than split on newlines, otherwise embedded newlines break the structure.
Mind whitespace and invisible characters. Text copied from web pages or PDFs often carries zero-width characters (U+200D–U+200D), non-breaking spaces (U+00A0) and curly quote variants, which cause text that looks identical to fail matching. Normalise before comparing.
Boundaries and Common Pitfalls
Invisible characters make identical-looking text fail to match
Text copied from web pages or Word documents often carries zero-width spaces (U+200B), zero-width joiners (U+200D), non-breaking spaces (U+00A0), BOMs and curly quote variants. The result is content surviving trim, string comparisons failing and ^abc$ not matching, with no visible cause. Defences: normalise to NFKC, strip zero-width characters and unify quotes to ASCII before storing or comparing, and print character codes rather than eyeballing when debugging.
Splitting CSV by line breaks breaks on embedded newlines
RFC 4180 permits newlines inside quoted fields, so splitting by line breaks cuts one record into two and leaves the field count wrong, corrupting everything downstream. Use a real CSV parser such as Python csv, papaparse or ExcelJS, which handle quote escaping, embedded newlines and delimiter escaping. Hand-rolled splitting is only safe for simple formats known to have no quotes or newlines.
Global replacement hits places it should not
A global replace of dots with commas also hits decimal points, version numbers like 1.2.3 and domain names. More subtly, a dot in a regex matches any character, so a.c matches abc and a1c — using that to convert separators corrupts data. The rules: count how many places a pattern matches before replacing anything, anchor with word boundaries or character classes (「\d+」 only matches decimals), and keep the original for rollback.
How This Relates to Neighbours
To run these operations, use the in-site text tools; this page is about choosing and the risks.
For encoding and mojibake, use the in-site character-encoding page; this one is about text processing rather than encoding.
Designing log collection redaction and parsing belongs to logging systems, out of scope here.
If the goal is debugging text that will not match, print character codes to look for invisible characters before suspecting the regular expression.
Practical Snippet
Key points: (1) text often carries zero-width characters (U+200B–U+200D), non-breaking spaces (U+00A0), BOMs and quote variants that make identical-looking text fail to match — normalise with NFKC and strip zero-width characters before comparing, and print character codes instead of eyeballing; (2) CSV needs a real parser because RFC 4180 permits newlines inside quoted fields, so splitting by line cuts one record in two; (3) count matches before replacing and anchor with character classes — a bare dot in a regex matches any character (a.c matches abc) and a global dot replacement corrupts decimals and version numbers; (4) deduplicate by business key, not by whole lines.
// ① 规范化:去不可见字符 + NFKC + 引号统一
const ZERO_WIDTH = /[-]/g
const QUOTES = { \u2018: "'", \u2019: "'", \u201C: '"', \u201D: '"' }
function normalizeText(s) {
return s
.replace(ZERO_WIDTH, '') // 零宽字符与 BOM
.replace(/ /g, ' ') // 不换行空格 → 普通空格
.replace(/[‘’]/g, "'") // 中文引号 → ASCII
.replace(/[“”]/g, '"')
.normalize('NFKC') // 全角 → 半角、兼容字符归一
.replace(/
/g, '
') // 换行统一
.trim()
}
console.log(normalizeText('ab c').length) // 3 而不是 5
// ② 调试时打印字符码而不是肉眼比对
function showCodes(s) {
return [...s].map(c => {
const cp = c.codePointAt(0)
const hex = cp.toString(16).toUpperCase().padStart(4, '0')
// 用普通字符串而非模板串:这一行只是演示如何显示字符码,
// 用模板串会与本页大量出现的美元符号加大括号字面量冲突(TS 会当插值解析)。
return hex + (cp < 128 ? ' ' : ' ')
}).join(' ')
}
// console.log(showCodes('ab')) → 0061 200B 0062
// ③ CSV 必须用解析器,不能按行 split(字段内可能有换行)
import { parse } from 'papaparse'
const csv = 'a,b
"line1
line2",c' // 第二行含引号包裹的换行
const wrong = csv.split('
').filter(Boolean) // ❌ 拆成 3 段
const right = parse(csv, { header: true }).data // ✅ 2 条记录
console.log(wrong.length, right.length) // 3 2
// ④ 替换前先统计命中数,且用字符类限定边界
function countMatches(text, re) {
return (text.match(new RegExp(re.source, 'g')) || []).length
}
const text = '1.5 and 1.2.3 and v1.0 and 2.25'
// ❌ /./g 会命中所有点:4 处
// ✅ 只匹配小数:d+.d+
console.log('所有点:', countMatches(text, /./g)) // 4
console.log('仅小数:', countMatches(text, /\d+\.\d+/g)) // 2
const fixed = text.replace(/d+.d+/g, m => m.replace('.', ','))
console.log(fixed) // 1,5 and 1.2.3 and v1.0 and 2,25 ← 版本号未被动
// ⑤ 去重排序要按业务键,不是按整行
const lines = ['b,2', 'a,1', 'b,2']
const seen = new Set()
const uniq = lines.filter(l => { if (seen.has(l)) return false; seen.add(l); return true })
console.log(uniq) // ['b,2', 'a,1'] —— 重复的 b,2 被去掉Practical Notes
- Define the comparison rule before deduplicatingDeduplicating by whole line, by line with whitespace stripped, or case-insensitively gives very different results. Never let dedup stand in for business semantics such as "keep the first occurrence" — with timestamped data, which row survives changes the analysis.
- Masking is irreversible, but length still leaks informationReplacing the middle digits of a phone number with **** leaves the remaining length and shape as information (enough to infer ranges and carriers). Real de-identification replaces the whole value or uses equal-length placeholders, and must cover logs too.
- Unicode normalization changes bytes without changing meaningThe same "é" can be one code point or an e plus a combining accent, and the two are not equal in a direct comparison. Normalize to NFC before comparing or deduplicating text, or you get "identical to the eye but not equal" surprises.
Data Sources
- Unicode UAX #15 (normalization)
- Unicode UTS #18 (regular expressions)
- Unicode Annex #11 (East Asian width)
Codes, names and definitions above come from public specifications; we claim no originality over the specification data itself. The arrangement, Chinese annotations and practical notes on this page are written by us.
Nine Common Text Operations
| Operation | What It Does | Typical Use |
|---|---|---|
| Text Stats | Count characters, words, lines, bytes and the longest line | Submission word limits, API payload length checks |
| Deduplicate Lines | Drop duplicate lines, usually keeping first occurrence order | Cleaning exported lists, merging keyword sheets |
| Remove Empty Lines | Strip blank lines and whitespace-only lines | Tidying text copied from PDFs or web pages |
| Text Diff | Compare two texts line by line or word by word | Reviewing config changes, manuscript edits |
| Obfuscate | Replace selected characters with asterisks or a mask | Hiding phone numbers, emails and keys before screenshots |
| Pluralise | Convert English nouns between singular and plural | Generating i18n copy, quantifiers in code |
| Normalise Email | Normalise case, split alias and domain, fix dots | Deduplicating signups, cleaning mailing lists |
| NATO Alphabet | Spell letters as Alpha, Bravo, Charlie and so on | Reading order numbers and codes over the phone |
| Lorem Ipsum | Generate filler paragraphs or words for layout | Design mockups, front-end component wiring |
Five Counting Metrics Compared
The same passage can yield numbers three times apart depending on the metric. Confirm which one is required before counting.
| Metric | Definition | Example |
|---|---|---|
| Characters | Unicode code points; one CJK character counts as 1 | "你好 hi" -> 4 |
| Bytes | Bytes after UTF-8 encoding; a CJK character is usually 3 | "你好 hi" -> 8 |
| Words | Split on whitespace; Chinese needs per-character or word segmentation | "你好 hi" -> 1 or 3 depending on the rule |
| Lines | Newline count plus one; mind CRLF versus LF | Two lines -> 2 |
| Display Width | Full-width characters count as 2, half-width as 1 | "你好 hi" -> 6 |
Frequently Asked Questions
How should Chinese text be counted?
There is no single right answer — it depends on the purpose. Publications and freelance payment usually count characters excluding whitespace, where one Han character is one unit. Database column limits are byte-based, and a Han character takes 3 bytes in UTF-8. Layout cares about display width, where a full-width character occupies two cells. Confirm which metric the other party wants; the results can differ by a factor of three.
Does removing duplicate lines preserve the original order?
Most tools preserve first-occurrence order by default: the first time a line is seen it stays in place, and later duplicates are dropped. If you want alphabetical order, sort first and then deduplicate. Conversely, if the order itself carries meaning — a log timeline, for example — do not sort first.
Is the NATO spelling alphabet still useful?
Yes, and it is remarkably practical for phone support and remote collaboration. It maps the 26 letters to words that are hard to confuse acoustically (A is Alpha, B is Bravo), specifically solving problems like b versus d or m versus n. Spelling out an order number, verification code or serial letter by letter is far more efficient than repeatedly asking "b or d?".
What is Lorem ipsum and why does everyone use it?
It is scrambled filler text that looks like Latin but carries no meaning, derived from Cicero and used by the typesetting trade since the sixteenth century. Its advantage is that readers are not distracted by content and instead focus on typography, leading and whitespace. For design mockups and front-end component wiring it also makes it easier to judge how Latin-script text will look.