Task: XXXX redacted


C++: #include <iostream>

using namespace std;

string redact(const string &source, const string &word, bool partial, bool insensitive, bool overkill) {
    string temp = source;

    auto different = [insensitive](char s, char w) {
        if (insensitive) {
            return toupper(s) != toupper(w);
        } else {
            return s != w;
        }
    };

    auto isWordChar = [](char c) {
        return c == '-' || isalpha(c);
    };

    for (size_t i = 0; i < temp.length() - word.length() + 1; i++) {
        bool match = true;
        for (size_t j = 0; j < word.length(); j++) {
            if (different(temp[i + j], word[j])) {
                match = false;
                break;
            }
        }
        if (match) {
            auto beg = i;
            auto end = i + word.length();

            if (!partial) {
                if (beg > 0 && isWordChar(temp[beg - 1])) {
                    continue;
                }
                if (end < temp.length() && isWordChar(temp[end])) {
                    continue;
                }
            }
            if (overkill) {
                while (beg > 0 && isWordChar(temp[beg - 1])) {
                    beg--;
                }
                while (end < temp.length() && isWordChar(temp[end])) {
                    end++;
                }
            }

            for (size_t k = beg; k < end; k++) {
                temp[k] = 'X';
            }
        }
    }

    return temp;
}

void example(const string &source, const string &word) {
    std::cout << "Redact " << word << '\n';
    std::cout << "[w|s|n] " << redact(source, word, false, false, false) << '\n';
    std::cout << "[w|i|n] " << redact(source, word, false, true, false) << '\n';
    std::cout << "[p|s|n] " << redact(source, word, true, false, false) << '\n';
    std::cout << "[p|i|n] " << redact(source, word, true, true, false) << '\n';
    std::cout << "[p|s|o] " << redact(source, word, true, false, true) << '\n';
    std::cout << "[p|i|o] " << redact(source, word, true, true, true) << '\n';
    std::cout << '\n';
}

int main() {
    string text = "Tom? Toms bottom tomato is in his stomach while playing the \"Tom-tom\" brand tom-toms. That's so tom";
    example(text, "Tom");
    example(text, "tom");
    return 0;
}



Go: package main

import (
    "fmt"
    "github.com/rivo/uniseg"
    "log"
    "regexp"
    "strings"
)

func join(words, seps []string) string {
    lw := len(words)
    ls := len(seps)
    if lw != ls+1 {
        log.Fatal("mismatch between number of words and separators")
    }
    var sb strings.Builder
    for i := 0; i < ls; i++ {
        sb.WriteString(words[i])
        sb.WriteString(seps[i])
    }
    sb.WriteString(words[lw-1])
    return sb.String()
}

func redact(text, word, opts string) {
    var partial, overkill bool
    exp := word
    if strings.IndexByte(opts, 'p') >= 0 {
        partial = true
    }
    if strings.IndexByte(opts, 'o') >= 0 {
        overkill = true
    }
    if strings.IndexByte(opts, 'i') >= 0 {
        exp = `(?i)` + exp
    }
    rgx := regexp.MustCompile(`[\s!-&(-,./:-@[-^{-~]+`) 
    seps := rgx.FindAllString(text, -1)
    words := rgx.Split(text, -1)
    rgx2 := regexp.MustCompile(exp)
    for i, w := range words {
        match := rgx2.FindString(w)
        
        if match == "" || strings.Index(w, match+"\u200d") >= 0 ||
            strings.Index(w, "\u200d"+match) >= 0 {
            continue
        }
        switch {
        case overkill:
            words[i] = strings.Repeat("X", uniseg.GraphemeClusterCount(w))
        case !partial:
            if words[i] == match {
                words[i] = strings.Repeat("X", uniseg.GraphemeClusterCount(w))
            }
        case partial:
            repl := strings.Repeat("X", uniseg.GraphemeClusterCount(word))
            words[i] = rgx2.ReplaceAllLiteralString(w, repl)
        }
    }
    fmt.Printf("%s %s\n\n", opts, join(words, seps))
}

func printResults(text string, allOpts, allWords []string) {
    fmt.Printf("Text: %s\n\n", text)
    for _, word := range allWords {
        fmt.Printf("Redact '%s':\n", word)
        for _, opts := range allOpts {
            redact(text, word, opts)
        }
    }
    fmt.Println()
}

func main() {
    text := `Tom? Toms bottom tomato is in his stomach while playing the "Tom-tom" brand tom-toms. That's so tom.
'Tis very tomish, don't you think?`
    allOpts := []string{"[w|s|n]", "[w|i|n]", "[p|s|n]", "[p|i|n]", "[p|s|o]", "[p|i|o]"}
    allWords := []string{"Tom", "tom", "t"}
    printResults(text, allOpts, allWords)

    text = "🧑 👨 🧔 👨‍👩‍👦"
    allOpts = []string{"[w]"}
    allWords = []string{"👨", "👨‍👩‍👦"}
    printResults(text, allOpts, allWords)

    text = "Argentina🧑🇦🇹  France👨🇫🇷  Germany🧔🇩🇪  Netherlands👨‍👩‍👦🇳🇱"
    allOpts = []string{"[p]", "[p|o]"}
    printResults(text, allOpts, allWords)
}