textregexdiff

Text Processing Tools for Developers

· Cosyslabs

Text processing is one of the most frequent developer tasks — transforming, comparing, analyzing, and encoding strings is central to building APIs, content management systems, data pipelines, and user interfaces. The right tool for each operation saves time and reduces bugs from manual string manipulation.

String Case Conversion

const str = "hello world example";

// camelCase
str.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, idx) =>
  idx === 0 ? word.toLowerCase() : word.toUpperCase()
).replace(/\s+/g, "");
// "helloWorldExample"

// PascalCase
str.replace(/(?:^\w|[A-Z]|\b\w)/g, w => w.toUpperCase()).replace(/\s+/g, "");
// "HelloWorldExample"

// snake_case
str.replace(/\s+/g, "_").toLowerCase();
// "hello_world_example"

// kebab-case
str.replace(/\s+/g, "-").toLowerCase();
// "hello-world-example"

// SCREAMING_SNAKE_CASE
str.replace(/\s+/g, "_").toUpperCase();
// "HELLO_WORLD_EXAMPLE"

// Converting between cases
"helloWorldExample"
  .replace(/([A-Z])/g, " $1")
  .trim()
  .toLowerCase()
  .replace(/\s+/g, "-");
// "hello-world-example"

Word and Character Count

const text = "Hello, world! This is a test.";

// Character count (including spaces)
text.length; // 30

// Character count (excluding spaces)
text.replace(/\s/g, "").length; // 25

// Word count
text.trim().split(/\s+/).length; // 6
// Better — handle multiple spaces and punctuation
text.trim().split(/\s+/).filter(w => w.length > 0).length;

// Sentence count
text.split(/[.!?]+/).filter(s => s.trim()).length; // 2

// Reading time (250 words/minute average)
const wordCount = text.trim().split(/\s+/).length;
const readingTimeMinutes = Math.ceil(wordCount / 250);
text = "Hello, world! This is a test."

len(text)                   # 30 — characters
len(text.replace(" ", ""))  # 26 — without spaces
len(text.split())           # 6 — words

String Escaping

Different contexts require different escaping strategies:

HTML Escaping

function escapeHtml(str) {
  const map = {
    "&": "&",
    "<": "&lt;",
    ">": "&gt;",
    '"': "&quot;",
    "'": "&#039;",
  };
  return str.replace(/[&<>"']/g, char => map[char]);
}

escapeHtml('<script>alert("XSS")</script>');
// "&lt;script&gt;alert(&quot;XSS&quot;)&lt;/script&gt;"

JSON String Escaping

// JSON.stringify handles this for you
JSON.stringify("He said \"hello\"\nNew line");
// '"He said \\"hello\\"\\nNew line"'

// For embedding JSON in HTML
JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e");

SQL String Escaping

Never build SQL by concatenating strings — use parameterized queries:

// WRONG — SQL injection vulnerability
db.query(`SELECT * FROM users WHERE name = '${userInput}'`);

// RIGHT — parameterized query
db.query("SELECT * FROM users WHERE name = $1", [userInput]);

Regex Escaping

function escapeRegex(str) {
  return str.replace(/[.*+?^${}()|[\]\\]/g, "\\
amp;"); } // Use when turning user input into a regex pattern const userSearch = "price: $10.00 (sale)"; const pattern = new RegExp(escapeRegex(userSearch), "i");

Shell Command Escaping

// Node.js — never pass user input directly to shell
// Wrong:
const { exec } = require("child_process");
exec(`grep ${userInput} /var/log/app.log`); // Shell injection!

// Right:
const { execFile } = require("child_process");
execFile("grep", [userInput, "/var/log/app.log"]);

Text Diff

// diff package
import { diffLines, diffWords, createPatch } from "diff";

const before = "Hello\nWorld\nFoo";
const after = "Hello\nEarth\nFoo\nBar";

// Line-by-line diff
const lineDiff = diffLines(before, after);
lineDiff.forEach(part => {
  const prefix = part.added ? "+" : part.removed ? "-" : " ";
  process.stdout.write(prefix + part.value);
});

// Word-level diff
const wordDiff = diffWords("the cat sat on the mat", "the cat sat on the floor");

// Create unified diff (Git-style)
const patch = createPatch("file.txt", before, after, "before", "after");
import difflib

before = "Hello\nWorld\nFoo\n"
after = "Hello\nEarth\nFoo\nBar\n"

# Unified diff
diff = list(difflib.unified_diff(
    before.splitlines(keepends=True),
    after.splitlines(keepends=True),
    fromfile="before",
    tofile="after"
))

# Sequence matcher
matcher = difflib.SequenceMatcher(None, before, after)
ratio = matcher.ratio()  # similarity ratio 0-1

Markdown Processing

// marked — lightweight Markdown parser
import { marked } from "marked";
import DOMPurify from "dompurify";

const markdown = "# Hello\n\nThis is **bold** and _italic_.";

// Parse to HTML
const rawHtml = marked.parse(markdown);

// Sanitize before inserting into DOM (critical for user content)
const safeHtml = DOMPurify.sanitize(rawHtml);
document.getElementById("output").innerHTML = safeHtml;
import markdown

md = "# Hello\n\nThis is **bold** and _italic_."
html = markdown.markdown(md, extensions=["extra", "codehilite"])

Lorem Ipsum Generation

Lorem ipsum is placeholder text used in layouts when real content is not yet available:

// Simple generator
const words = [
  "lorem", "ipsum", "dolor", "sit", "amet", "consectetur",
  "adipiscing", "elit", "sed", "do", "eiusmod", "tempor",
  "incididunt", "ut", "labore", "et", "dolore", "magna", "aliqua"
];

function generateLorem(wordCount = 50) {
  const result = [];
  for (let i = 0; i < wordCount; i++) {
    result.push(words[Math.floor(Math.random() * words.length)]);
  }
  const sentence = result.join(" ");
  return sentence.charAt(0).toUpperCase() + sentence.slice(1) + ".";
}

String Truncation

// Simple truncation
function truncate(str, maxLength, suffix = "...") {
  if (str.length <= maxLength) return str;
  return str.slice(0, maxLength - suffix.length) + suffix;
}

// Word-aware truncation (avoid cutting mid-word)
function truncateAtWord(str, maxLength, suffix = "...") {
  if (str.length <= maxLength) return str;
  const truncated = str.slice(0, maxLength - suffix.length);
  const lastSpace = truncated.lastIndexOf(" ");
  return (lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated) + suffix;
}

// CSS-only truncation (single line)
// overflow: hidden; white-space: nowrap; text-overflow: ellipsis;

// CSS multi-line truncation (WebKit)
// overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;

Unicode and Emoji Handling

// String length is wrong for emoji!
"Hello 👋".length; // 8 (but visually 7 characters)
[..."Hello 👋"].length; // 7 (correct — spread uses Unicode code points)

// Count code points correctly
function countChars(str) {
  return [...str].length;
}

// Reverse string correctly
function reverseString(str) {
  return [...str].reverse().join("");
}

// Check for emoji
const hasEmoji = /\p{Emoji}/u.test(str);

// Normalize Unicode (e.g., for search/comparison)
const normalized = str.normalize("NFC"); // or NFD, NFKC, NFKD

Slug Generation

function slugify(str) {
  return str
    .toLowerCase()
    .normalize("NFD")                          // decompose accents
    .replace(/[̀-ͯ]/g, "")          // remove accent marks
    .replace(/[^a-z0-9\s-]/g, "")            // remove non-alphanumeric
    .trim()
    .replace(/[\s_-]+/g, "-")               // spaces to hyphens
    .replace(/^-+|-+$/g, "");               // trim leading/trailing hyphens
}

slugify("Héllo Wörld! How are you?");
// "hello-world-how-are-you"

Text Statistics

function analyzeText(text) {
  const words = text.trim().split(/\s+/).filter(Boolean);
  const sentences = text.split(/[.!?]+/).filter(s => s.trim());
  const paragraphs = text.split(/\n\n+/).filter(p => p.trim());
  
  return {
    characters: text.length,
    charactersNoSpaces: text.replace(/\s/g, "").length,
    words: words.length,
    sentences: sentences.length,
    paragraphs: paragraphs.length,
    avgWordsPerSentence: (words.length / sentences.length).toFixed(1),
    readingTimeMinutes: Math.ceil(words.length / 250),
    speakingTimeMinutes: Math.ceil(words.length / 130),
  };
}

Tools