Rebol3 Code Examplex


XXXX redacted

Remove or mask sensitive text from content.

Rebol [
    title: "Rosetta code: XXXX redacted"
    file:  %XXXX_redacted.r3
    url:   https://rosettacode.org/wiki/XXXX_redacted
    needs: 3.21.0 ;; Unicode
]

radect-text: function/with [
    mode [any-word! string! ref!] "Any: wsn win psn pin pso pio"
    word [string!]                "Word to hide"
    text [string! file! url!]
][
    unless string? text [text: read/string text]
    mode:  form mode
    rplc:  append/dup clear "" #"X" length? word  ;; replacement: "XXX" same length as word
    case:  mode/2 = #"s"                          ;; s = case-sensitive, n = case-insensitive
    either mode/3 = #"o" [
        ;; Overkill: redact the entire token containing the match
        rule: [
            to word s: (
                unless head? s [s: find/reverse/tail s o-chars]   ;; back up to token start
                e: any [find s o-chars tail s]                    ;; scan forward to token end
                rplc: append/dup clear "" #"X" len: (index? e) - (index? s)  ;; fit XXX to token length
                e: change/part s rplc e
            ) :e
        ]
    ][  ;; Whole / partial match
        rule: either/only mode/1 = #"w" [
            ahead [word w-chars] change word rplc | thru delimit  ;; whole: match word at boundary
        ][                       change word rplc | skip]         ;; partial: match anywhere
    ]
    parse/:case text [any rule]
    text
][  ;; Static charset definitions (shared across all calls)
    w-chars: charset { ^-^/.,;:?}         ;; chars that may follow a whole word
    delimit: charset "- .;:?^/^"[](){}"   ;; token delimiters for whole-word skipping
    o-chars: charset  " .;:?^/^"[](){}"   ;; token boundaries for overkill mode
]

;; Test cases:
text: {Tom? Toms bottom tomato is in his stomach while playing the "Tom-tom" brand tom-toms. That's so tom.}
foreach word ["Tom" "tom"][
    print ["Redact" as-green word ":"]
    foreach mode [wsn win psn pin pso pio][   ;; w/p=whole/partial, s/n=case, o=overkill
        print [mode radect-text :mode :word copy :text]
    ]
]
print ["Redact" "👨" ":"]
probe radect-text 'w "👨" "🧑 👨 🧔 👨‍👩‍👦"   ;; sanity-check Unicode handling

Output:

Redact Tom :
wsn XXX? Toms bottom tomato is in his stomach while playing the "Tom-tom" brand tom-toms. That's so tom.
win XXX? Toms bottom tomato is in his stomach while playing the "Tom-tom" brand tom-toms. That's so XXX.
psn XXX? XXXs bottom tomato is in his stomach while playing the "XXX-tom" brand tom-toms. That's so tom.
pin XXX? XXXs botXXX XXXato is in his sXXXach while playing the "XXX-XXX" brand XXX-XXXs. That's so XXX.
pso XXX? XXXX bottom tomato is in his stomach while playing the "XXXXXXX" brand tom-toms. That's so tom.
pio XXX? XXXX XXXXXX XXXXXX is in his XXXXXXX while playing the "XXXXXXX" brand XXXXXXXX. That's so XXX.
Redact tom :
wsn Tom? Toms bottom tomato is in his stomach while playing the "Tom-tom" brand tom-toms. That's so XXX.
win XXX? Toms bottom tomato is in his stomach while playing the "Tom-tom" brand tom-toms. That's so XXX.
psn Tom? Toms botXXX XXXato is in his sXXXach while playing the "Tom-XXX" brand XXX-XXXs. That's so XXX.
pin XXX? XXXs botXXX XXXato is in his sXXXach while playing the "XXX-XXX" brand XXX-XXXs. That's so XXX.
pso Tom? Toms XXXXXX XXXXXX is in his XXXXXXX while playing the "XXXXXXX" brand XXXXXXXX. That's so XXX.
pio XXX? XXXX XXXXXX XXXXXX is in his XXXXXXX while playing the "XXXXXXX" brand XXXXXXXX. That's so XXX.
Redact 👨 :
"🧑 X 🧔 👨‍👩‍👦"