File size: 4,129 Bytes
5b97e57 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 | import re
from typing import Tuple
HELP_TEXT = """Examples:
Find => Replace pairs (one per line):
hello => hi
world => earth
old => new
Empty replacement removes text:
remove_this =>
Note: Replacements are applied sequentially.
If 'A => B' and 'B => C' are pairs, 'A' will become 'C'."""
REGEX_HELP = """Common Patterns:
. = any character
.* = any characters
\\d = digit
\\w = word character
[abc] = a, b, or c
(text) = capture group
"""
class SimpleStringReplace:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"forceInput": True}),
"find_replace_pairs": ("STRING", {
"multiline": True,
"default": "find => replace\nold => new",
"placeholder": HELP_TEXT
}),
},
"optional": {
"whole_word_match": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "replace"
CATEGORY = "string"
def replace(self, text: str, find_replace_pairs: str, whole_word_match: bool = False) -> Tuple[str]:
if not text:
return (text,)
result = text
lines = [line.strip() for line in find_replace_pairs.split('\n') if line.strip()]
for line in lines:
if '=>' not in line:
continue
find, replace = line.split('=>', 1)
find = find.strip()
replace = replace.strip()
if find:
if whole_word_match:
pattern = r'\b' + re.escape(find) + r'\b'
result = re.sub(pattern, replace, result)
else:
result = result.replace(find, replace)
return (result,)
class RegexStringReplace:
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"text": ("STRING", {"forceInput": True}),
"pattern": ("STRING", {
"multiline": True,
"placeholder": REGEX_HELP
}),
"replacement": ("STRING", {"multiline": True}),
},
"optional": {
"case_insensitive": ("BOOLEAN", {"default": False}),
"test_mode": ("BOOLEAN", {"default": False}),
}
}
RETURN_TYPES = ("STRING",)
FUNCTION = "replace"
CATEGORY = "string"
def replace(self, text: str, pattern: str, replacement: str,
case_insensitive: bool = False, test_mode: bool = False) -> Tuple[str]:
if not text or not pattern:
return (text,)
try:
flags = re.IGNORECASE if case_insensitive else 0
if test_mode:
# In test mode, highlight matches without replacing
matches = re.finditer(pattern, text, flags=flags)
result = text
highlight_start = ">>>"
highlight_end = "<<<"
# Work backwards through matches to not mess up positions
matches = list(matches)
for match in reversed(matches):
start, end = match.span()
result = result[:start] + highlight_start + result[start:end] + highlight_end + result[end:]
return (result,)
else:
result = re.sub(pattern, replacement, text, flags=flags)
return (result,)
except re.error as e:
return (f"Error in regex pattern: {str(e)}\nOriginal text:\n{text}",)
NODE_CLASS_MAPPINGS = {
"SimpleStringReplace": SimpleStringReplace,
"RegexStringReplace": RegexStringReplace
}
NODE_DISPLAY_NAME_MAPPINGS = {
"SimpleStringReplace": "Simple String Replace",
"RegexStringReplace": "Regex String Replace"
}
|