Download comfyui-moonpack/string_replace_nodes.py from MarkLilly/mis-custom-nodes: direct link, hf CLI and curl.
- Browser
- Download file 4.13 kB
-
https://huggingface.co/MarkLilly/mis-custom-nodes/resolve/main/comfyui-moonpack/string_replace_nodes.py
- Command line
-
hf download hf://MarkLilly/mis-custom-nodes/comfyui-moonpack/string_replace_nodes.py
-
curl -L -o string_replace_nodes.py https://huggingface.co/MarkLilly/mis-custom-nodes/resolve/main/comfyui-moonpack/string_replace_nodes.py
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: | |
| 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: | |
| 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" | |
| } | |