mis-custom-nodes / comfyui-moonpack /string_replace_nodes.py
MarkLilly's picture
Add files using upload-large-folder tool
5b97e57 verified
Raw History Blame Contribute Delete
4.13 kB
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"
}