mirror of
https://git.datalinker.icu/comfyanonymous/ComfyUI
synced 2026-09-03 01:47:05 +08:00
migrate nodes_string to v3
This commit is contained in:
parent
aff5271291
commit
8a3544ce0c
@ -1,77 +1,175 @@
|
|||||||
|
"""
|
||||||
|
String manipulation nodes converted to ComfyUI v3 format.
|
||||||
|
|
||||||
|
This module contains v3 conversions of all string manipulation nodes from nodes_string.py.
|
||||||
|
The v3 implementations provide type safety, better documentation, and cleaner APIs
|
||||||
|
while maintaining full backward compatibility with v1 through the automatic
|
||||||
|
compatibility layer.
|
||||||
|
"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from comfy_api.v3 import io
|
||||||
|
|
||||||
from comfy.comfy_types.node_typing import IO
|
|
||||||
|
|
||||||
class StringConcatenate():
|
class StringConcatenate(io.ComfyNodeV3):
|
||||||
|
"""Concatenates two strings with an optional delimiter between them."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="StringConcatenate",
|
||||||
"string_a": (IO.STRING, {"multiline": True}),
|
display_name="String Concatenate",
|
||||||
"string_b": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"delimiter": (IO.STRING, {"multiline": False, "default": ""})
|
description="Concatenates two strings together with an optional delimiter between them.",
|
||||||
}
|
inputs=[
|
||||||
}
|
io.String.Input(
|
||||||
|
"string_a",
|
||||||
|
display_name="String A",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The first string to concatenate",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"string_b",
|
||||||
|
display_name="String B",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The second string to concatenate",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"delimiter",
|
||||||
|
display_name="Delimiter",
|
||||||
|
default="",
|
||||||
|
multiline=False,
|
||||||
|
tooltip="The delimiter to insert between the two strings (empty by default)",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"concatenated",
|
||||||
|
display_name="Concatenated String",
|
||||||
|
tooltip="The result of concatenating string_a and string_b with the delimiter",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string_a, string_b, delimiter, **kwargs):
|
|
||||||
return delimiter.join((string_a, string_b)),
|
|
||||||
|
|
||||||
class StringSubstring():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def execute(cls, string_a: str, string_b: str, delimiter: str) -> io.NodeOutput:
|
||||||
return {
|
"""Concatenates two strings with an optional delimiter."""
|
||||||
"required": {
|
result = delimiter.join((string_a, string_b))
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
return io.NodeOutput(result)
|
||||||
"start": (IO.INT, {}),
|
|
||||||
"end": (IO.INT, {}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, start, end, **kwargs):
|
class StringSubstring(io.ComfyNodeV3):
|
||||||
return string[start:end],
|
"""Extracts a substring from a string using start and end indices."""
|
||||||
|
|
||||||
class StringLength():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="StringSubstring",
|
||||||
"string": (IO.STRING, {"multiline": True})
|
display_name="String Substring",
|
||||||
}
|
category="utils/string",
|
||||||
}
|
description="Extracts a portion of a string using Python slice notation [start:end].",
|
||||||
|
inputs=[
|
||||||
|
io.String.Input(
|
||||||
|
"string",
|
||||||
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to extract a substring from",
|
||||||
|
),
|
||||||
|
io.Int.Input(
|
||||||
|
"start",
|
||||||
|
display_name="Start Index",
|
||||||
|
tooltip="Starting position (inclusive). Negative values count from the end",
|
||||||
|
),
|
||||||
|
io.Int.Input(
|
||||||
|
"end",
|
||||||
|
display_name="End Index",
|
||||||
|
tooltip="Ending position (exclusive). Negative values count from the end",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"substring",
|
||||||
|
display_name="Substring",
|
||||||
|
tooltip="The extracted substring",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.INT,)
|
|
||||||
RETURN_NAMES = ("length",)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, **kwargs):
|
|
||||||
length = len(string)
|
|
||||||
|
|
||||||
return length,
|
|
||||||
|
|
||||||
class CaseConverter():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def execute(cls, string: str, start: int, end: int) -> io.NodeOutput:
|
||||||
return {
|
"""Extracts substring using Python slice notation."""
|
||||||
"required": {
|
return io.NodeOutput(string[start:end])
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
|
||||||
"mode": (IO.COMBO, {"options": ["UPPERCASE", "lowercase", "Capitalize", "Title Case"]})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
|
||||||
FUNCTION = "execute"
|
|
||||||
CATEGORY = "utils/string"
|
|
||||||
|
|
||||||
def execute(self, string, mode, **kwargs):
|
class StringLength(io.ComfyNodeV3):
|
||||||
|
"""Returns the length of a string."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def DEFINE_SCHEMA(cls):
|
||||||
|
return io.SchemaV3(
|
||||||
|
node_id="StringLength",
|
||||||
|
display_name="String Length",
|
||||||
|
category="utils/string",
|
||||||
|
description="Calculates the number of characters in a string.",
|
||||||
|
inputs=[
|
||||||
|
io.String.Input(
|
||||||
|
"string",
|
||||||
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to measure",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Int.Output(
|
||||||
|
"length",
|
||||||
|
display_name="Length",
|
||||||
|
tooltip="The number of characters in the string",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def execute(cls, string: str) -> io.NodeOutput:
|
||||||
|
"""Returns the length of the input string."""
|
||||||
|
return io.NodeOutput(len(string))
|
||||||
|
|
||||||
|
|
||||||
|
class CaseConverter(io.ComfyNodeV3):
|
||||||
|
"""Converts string case to uppercase, lowercase, capitalize, or title case."""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def DEFINE_SCHEMA(cls):
|
||||||
|
return io.SchemaV3(
|
||||||
|
node_id="CaseConverter",
|
||||||
|
display_name="Case Converter",
|
||||||
|
category="utils/string",
|
||||||
|
description="Converts text to different case formats.",
|
||||||
|
inputs=[
|
||||||
|
io.String.Input(
|
||||||
|
"string",
|
||||||
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to convert",
|
||||||
|
),
|
||||||
|
io.Combo.Input(
|
||||||
|
"mode",
|
||||||
|
display_name="Mode",
|
||||||
|
options=["UPPERCASE", "lowercase", "Capitalize", "Title Case"],
|
||||||
|
tooltip="The case conversion mode to apply",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"converted",
|
||||||
|
display_name="Converted String",
|
||||||
|
tooltip="The string with the selected case conversion applied",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def execute(cls, string: str, mode: str) -> io.NodeOutput:
|
||||||
|
"""Converts string to the selected case format."""
|
||||||
if mode == "UPPERCASE":
|
if mode == "UPPERCASE":
|
||||||
result = string.upper()
|
result = string.upper()
|
||||||
elif mode == "lowercase":
|
elif mode == "lowercase":
|
||||||
@ -83,24 +181,45 @@ class CaseConverter():
|
|||||||
else:
|
else:
|
||||||
result = string
|
result = string
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class StringTrim():
|
class StringTrim(io.ComfyNodeV3):
|
||||||
|
"""Removes whitespace from the beginning, end, or both sides of a string."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="StringTrim",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="String Trim",
|
||||||
"mode": (IO.COMBO, {"options": ["Both", "Left", "Right"]})
|
category="utils/string",
|
||||||
}
|
description="Removes leading and/or trailing whitespace from a string.",
|
||||||
}
|
inputs=[
|
||||||
|
io.String.Input(
|
||||||
|
"string",
|
||||||
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to trim",
|
||||||
|
),
|
||||||
|
io.Combo.Input(
|
||||||
|
"mode",
|
||||||
|
display_name="Mode",
|
||||||
|
options=["Both", "Left", "Right"],
|
||||||
|
tooltip="Which side(s) to trim whitespace from",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"trimmed",
|
||||||
|
display_name="Trimmed String",
|
||||||
|
tooltip="The string with whitespace removed",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(cls, string: str, mode: str) -> io.NodeOutput:
|
||||||
CATEGORY = "utils/string"
|
"""Removes whitespace based on the selected mode."""
|
||||||
|
|
||||||
def execute(self, string, mode, **kwargs):
|
|
||||||
if mode == "Both":
|
if mode == "Both":
|
||||||
result = string.strip()
|
result = string.strip()
|
||||||
elif mode == "Left":
|
elif mode == "Left":
|
||||||
@ -110,70 +229,157 @@ class StringTrim():
|
|||||||
else:
|
else:
|
||||||
result = string
|
result = string
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
|
class StringReplace(io.ComfyNodeV3):
|
||||||
|
"""Replaces all occurrences of a substring with another string."""
|
||||||
|
|
||||||
class StringReplace():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="StringReplace",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="String Replace",
|
||||||
"find": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"replace": (IO.STRING, {"multiline": True})
|
description="Replaces all occurrences of a substring within a string.",
|
||||||
}
|
inputs=[
|
||||||
}
|
io.String.Input(
|
||||||
|
"string",
|
||||||
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to search in",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"find",
|
||||||
|
display_name="Find",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The substring to search for",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"replace",
|
||||||
|
display_name="Replace",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to replace matches with",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"result",
|
||||||
|
display_name="Result",
|
||||||
|
tooltip="The string with all replacements made",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(cls, string: str, find: str, replace: str) -> io.NodeOutput:
|
||||||
CATEGORY = "utils/string"
|
"""Replaces all occurrences of find with replace."""
|
||||||
|
|
||||||
def execute(self, string, find, replace, **kwargs):
|
|
||||||
result = string.replace(find, replace)
|
result = string.replace(find, replace)
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class StringContains():
|
class StringContains(io.ComfyNodeV3):
|
||||||
|
"""Checks if a string contains a substring."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="StringContains",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="String Contains",
|
||||||
"substring": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"case_sensitive": (IO.BOOLEAN, {"default": True})
|
description="Checks whether a string contains a specific substring.",
|
||||||
}
|
inputs=[
|
||||||
}
|
io.String.Input(
|
||||||
|
"string",
|
||||||
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to search in",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"substring",
|
||||||
|
display_name="Substring",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The substring to search for",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"case_sensitive",
|
||||||
|
display_name="Case Sensitive",
|
||||||
|
default=True,
|
||||||
|
tooltip="Whether the search should be case sensitive",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Boolean.Output(
|
||||||
|
"contains",
|
||||||
|
display_name="Contains",
|
||||||
|
tooltip="True if the substring is found, False otherwise",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.BOOLEAN,)
|
@classmethod
|
||||||
RETURN_NAMES = ("contains",)
|
def execute(
|
||||||
FUNCTION = "execute"
|
cls, string: str, substring: str, case_sensitive: bool
|
||||||
CATEGORY = "utils/string"
|
) -> io.NodeOutput:
|
||||||
|
"""Checks if string contains substring with optional case sensitivity."""
|
||||||
def execute(self, string, substring, case_sensitive, **kwargs):
|
|
||||||
if case_sensitive:
|
if case_sensitive:
|
||||||
contains = substring in string
|
contains = substring in string
|
||||||
else:
|
else:
|
||||||
contains = substring.lower() in string.lower()
|
contains = substring.lower() in string.lower()
|
||||||
|
|
||||||
return contains,
|
return io.NodeOutput(contains)
|
||||||
|
|
||||||
|
|
||||||
class StringCompare():
|
class StringCompare(io.ComfyNodeV3):
|
||||||
|
"""Compares two strings with various comparison modes."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="StringCompare",
|
||||||
"string_a": (IO.STRING, {"multiline": True}),
|
display_name="String Compare",
|
||||||
"string_b": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"mode": (IO.COMBO, {"options": ["Starts With", "Ends With", "Equal"]}),
|
description="Compares two strings using different comparison modes.",
|
||||||
"case_sensitive": (IO.BOOLEAN, {"default": True})
|
inputs=[
|
||||||
}
|
io.String.Input(
|
||||||
}
|
"string_a",
|
||||||
|
display_name="String A",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The first string to compare",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"string_b",
|
||||||
|
display_name="String B",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The second string to compare",
|
||||||
|
),
|
||||||
|
io.Combo.Input(
|
||||||
|
"mode",
|
||||||
|
display_name="Mode",
|
||||||
|
options=["Starts With", "Ends With", "Equal"],
|
||||||
|
tooltip="The comparison mode to use",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"case_sensitive",
|
||||||
|
display_name="Case Sensitive",
|
||||||
|
default=True,
|
||||||
|
tooltip="Whether the comparison should be case sensitive",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Boolean.Output(
|
||||||
|
"result",
|
||||||
|
display_name="Result",
|
||||||
|
tooltip="True if the comparison succeeds, False otherwise",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.BOOLEAN,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(
|
||||||
CATEGORY = "utils/string"
|
cls, string_a: str, string_b: str, mode: str, case_sensitive: bool
|
||||||
|
) -> io.NodeOutput:
|
||||||
def execute(self, string_a, string_b, mode, case_sensitive, **kwargs):
|
"""Compares two strings based on the selected mode and case sensitivity."""
|
||||||
if case_sensitive:
|
if case_sensitive:
|
||||||
a = string_a
|
a = string_a
|
||||||
b = string_b
|
b = string_b
|
||||||
@ -182,31 +388,78 @@ class StringCompare():
|
|||||||
b = string_b.lower()
|
b = string_b.lower()
|
||||||
|
|
||||||
if mode == "Equal":
|
if mode == "Equal":
|
||||||
return a == b,
|
result = a == b
|
||||||
elif mode == "Starts With":
|
elif mode == "Starts With":
|
||||||
return a.startswith(b),
|
result = a.startswith(b)
|
||||||
elif mode == "Ends With":
|
elif mode == "Ends With":
|
||||||
return a.endswith(b),
|
result = a.endswith(b)
|
||||||
|
else:
|
||||||
|
result = False
|
||||||
|
|
||||||
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
|
class RegexMatch(io.ComfyNodeV3):
|
||||||
|
"""Tests if a string matches a regular expression pattern."""
|
||||||
|
|
||||||
class RegexMatch():
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="RegexMatch",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Regex Match",
|
||||||
"regex_pattern": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"case_insensitive": (IO.BOOLEAN, {"default": True}),
|
description="Tests whether a string matches a regular expression pattern.",
|
||||||
"multiline": (IO.BOOLEAN, {"default": False}),
|
inputs=[
|
||||||
"dotall": (IO.BOOLEAN, {"default": False})
|
io.String.Input(
|
||||||
}
|
"string",
|
||||||
}
|
display_name="String",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The string to test",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"regex_pattern",
|
||||||
|
display_name="Regex Pattern",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The regular expression pattern to match against",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"case_insensitive",
|
||||||
|
display_name="Case Insensitive",
|
||||||
|
default=True,
|
||||||
|
tooltip="Whether to ignore case when matching",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"multiline",
|
||||||
|
display_name="Multiline",
|
||||||
|
default=False,
|
||||||
|
tooltip="Whether ^ and $ match line boundaries",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"dotall",
|
||||||
|
display_name="Dot All",
|
||||||
|
default=False,
|
||||||
|
tooltip="Whether . matches newline characters",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.Boolean.Output(
|
||||||
|
"matches",
|
||||||
|
display_name="Matches",
|
||||||
|
tooltip="True if the pattern matches, False otherwise",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.BOOLEAN,)
|
@classmethod
|
||||||
RETURN_NAMES = ("matches",)
|
def execute(
|
||||||
FUNCTION = "execute"
|
cls,
|
||||||
CATEGORY = "utils/string"
|
string: str,
|
||||||
|
regex_pattern: str,
|
||||||
def execute(self, string, regex_pattern, case_insensitive, multiline, dotall, **kwargs):
|
case_insensitive: bool,
|
||||||
|
multiline: bool,
|
||||||
|
dotall: bool,
|
||||||
|
) -> io.NodeOutput:
|
||||||
|
"""Tests if string matches the regex pattern."""
|
||||||
flags = 0
|
flags = 0
|
||||||
|
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
@ -219,33 +472,89 @@ class RegexMatch():
|
|||||||
try:
|
try:
|
||||||
match = re.search(regex_pattern, string, flags)
|
match = re.search(regex_pattern, string, flags)
|
||||||
result = match is not None
|
result = match is not None
|
||||||
|
|
||||||
except re.error:
|
except re.error:
|
||||||
result = False
|
result = False
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class RegexExtract():
|
class RegexExtract(io.ComfyNodeV3):
|
||||||
|
"""Extracts text from a string using regular expression patterns."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="RegexExtract",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Regex Extract",
|
||||||
"regex_pattern": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"mode": (IO.COMBO, {"options": ["First Match", "All Matches", "First Group", "All Groups"]}),
|
description="Extracts text from a string using regular expression patterns and groups.",
|
||||||
"case_insensitive": (IO.BOOLEAN, {"default": True}),
|
inputs=[
|
||||||
"multiline": (IO.BOOLEAN, {"default": False}),
|
io.String.Input(
|
||||||
"dotall": (IO.BOOLEAN, {"default": False}),
|
"string",
|
||||||
"group_index": (IO.INT, {"default": 1, "min": 0, "max": 100})
|
display_name="String",
|
||||||
}
|
multiline=True,
|
||||||
}
|
tooltip="The string to extract from",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"regex_pattern",
|
||||||
|
display_name="Regex Pattern",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The regular expression pattern with optional groups",
|
||||||
|
),
|
||||||
|
io.Combo.Input(
|
||||||
|
"mode",
|
||||||
|
display_name="Mode",
|
||||||
|
options=["First Match", "All Matches", "First Group", "All Groups"],
|
||||||
|
tooltip="What to extract from the matches",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"case_insensitive",
|
||||||
|
display_name="Case Insensitive",
|
||||||
|
default=True,
|
||||||
|
tooltip="Whether to ignore case when matching",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"multiline",
|
||||||
|
display_name="Multiline",
|
||||||
|
default=False,
|
||||||
|
tooltip="Whether ^ and $ match line boundaries",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"dotall",
|
||||||
|
display_name="Dot All",
|
||||||
|
default=False,
|
||||||
|
tooltip="Whether . matches newline characters",
|
||||||
|
),
|
||||||
|
io.Int.Input(
|
||||||
|
"group_index",
|
||||||
|
display_name="Group Index",
|
||||||
|
default=1,
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
tooltip="Which capture group to extract (0 = entire match)",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"extracted",
|
||||||
|
display_name="Extracted",
|
||||||
|
tooltip="The extracted text (multiple matches joined with newlines)",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(
|
||||||
CATEGORY = "utils/string"
|
cls,
|
||||||
|
string: str,
|
||||||
def execute(self, string, regex_pattern, mode, case_insensitive, multiline, dotall, group_index, **kwargs):
|
regex_pattern: str,
|
||||||
|
mode: str,
|
||||||
|
case_insensitive: bool,
|
||||||
|
multiline: bool,
|
||||||
|
dotall: bool,
|
||||||
|
group_index: int,
|
||||||
|
) -> io.NodeOutput:
|
||||||
|
"""Extracts text based on regex pattern and mode."""
|
||||||
join_delimiter = "\n"
|
join_delimiter = "\n"
|
||||||
|
|
||||||
flags = 0
|
flags = 0
|
||||||
@ -294,32 +603,90 @@ class RegexExtract():
|
|||||||
except re.error:
|
except re.error:
|
||||||
result = ""
|
result = ""
|
||||||
|
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
class RegexReplace():
|
class RegexReplace(io.ComfyNodeV3):
|
||||||
DESCRIPTION = "Find and replace text using regex patterns."
|
"""Find and replace text using regex patterns."""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def INPUT_TYPES(s):
|
def DEFINE_SCHEMA(cls):
|
||||||
return {
|
return io.SchemaV3(
|
||||||
"required": {
|
node_id="RegexReplace",
|
||||||
"string": (IO.STRING, {"multiline": True}),
|
display_name="Regex Replace",
|
||||||
"regex_pattern": (IO.STRING, {"multiline": True}),
|
category="utils/string",
|
||||||
"replace": (IO.STRING, {"multiline": True}),
|
description="Find and replace text using regular expression patterns.",
|
||||||
},
|
inputs=[
|
||||||
"optional": {
|
io.String.Input(
|
||||||
"case_insensitive": (IO.BOOLEAN, {"default": True}),
|
"string",
|
||||||
"multiline": (IO.BOOLEAN, {"default": False}),
|
display_name="String",
|
||||||
"dotall": (IO.BOOLEAN, {"default": False, "tooltip": "When enabled, the dot (.) character will match any character including newline characters. When disabled, dots won't match newlines."}),
|
multiline=True,
|
||||||
"count": (IO.INT, {"default": 0, "min": 0, "max": 100, "tooltip": "Maximum number of replacements to make. Set to 0 to replace all occurrences (default). Set to 1 to replace only the first match, 2 for the first two matches, etc."}),
|
tooltip="The string to perform replacements on",
|
||||||
}
|
),
|
||||||
}
|
io.String.Input(
|
||||||
|
"regex_pattern",
|
||||||
|
display_name="Regex Pattern",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The regular expression pattern to match",
|
||||||
|
),
|
||||||
|
io.String.Input(
|
||||||
|
"replace",
|
||||||
|
display_name="Replace",
|
||||||
|
multiline=True,
|
||||||
|
tooltip="The replacement text (can use \\1, \\2 for capture groups)",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"case_insensitive",
|
||||||
|
display_name="Case Insensitive",
|
||||||
|
default=True,
|
||||||
|
optional=True,
|
||||||
|
tooltip="Whether to ignore case when matching",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"multiline",
|
||||||
|
display_name="Multiline",
|
||||||
|
default=False,
|
||||||
|
optional=True,
|
||||||
|
tooltip="Whether ^ and $ match line boundaries",
|
||||||
|
),
|
||||||
|
io.Boolean.Input(
|
||||||
|
"dotall",
|
||||||
|
display_name="Dot All",
|
||||||
|
default=False,
|
||||||
|
optional=True,
|
||||||
|
tooltip="When enabled, the dot (.) character will match any character including newline characters. When disabled, dots won't match newlines.",
|
||||||
|
),
|
||||||
|
io.Int.Input(
|
||||||
|
"count",
|
||||||
|
display_name="Count",
|
||||||
|
default=0,
|
||||||
|
min=0,
|
||||||
|
max=100,
|
||||||
|
optional=True,
|
||||||
|
tooltip="Maximum number of replacements to make. Set to 0 to replace all occurrences (default). Set to 1 to replace only the first match, 2 for the first two matches, etc.",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
io.String.Output(
|
||||||
|
"result",
|
||||||
|
display_name="Result",
|
||||||
|
tooltip="The string with replacements made",
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
RETURN_TYPES = (IO.STRING,)
|
@classmethod
|
||||||
FUNCTION = "execute"
|
def execute(
|
||||||
CATEGORY = "utils/string"
|
cls,
|
||||||
|
string: str,
|
||||||
def execute(self, string, regex_pattern, replace, case_insensitive=True, multiline=False, dotall=False, count=0, **kwargs):
|
regex_pattern: str,
|
||||||
|
replace: str,
|
||||||
|
case_insensitive: bool = True,
|
||||||
|
multiline: bool = False,
|
||||||
|
dotall: bool = False,
|
||||||
|
count: int = 0,
|
||||||
|
) -> io.NodeOutput:
|
||||||
|
"""Replaces text matching regex pattern."""
|
||||||
flags = 0
|
flags = 0
|
||||||
|
|
||||||
if case_insensitive:
|
if case_insensitive:
|
||||||
@ -328,8 +695,10 @@ class RegexReplace():
|
|||||||
flags |= re.MULTILINE
|
flags |= re.MULTILINE
|
||||||
if dotall:
|
if dotall:
|
||||||
flags |= re.DOTALL
|
flags |= re.DOTALL
|
||||||
|
|
||||||
result = re.sub(regex_pattern, replace, string, count=count, flags=flags)
|
result = re.sub(regex_pattern, replace, string, count=count, flags=flags)
|
||||||
return result,
|
return io.NodeOutput(result)
|
||||||
|
|
||||||
|
|
||||||
NODE_CLASS_MAPPINGS = {
|
NODE_CLASS_MAPPINGS = {
|
||||||
"StringConcatenate": StringConcatenate,
|
"StringConcatenate": StringConcatenate,
|
||||||
@ -358,3 +727,4 @@ NODE_DISPLAY_NAME_MAPPINGS = {
|
|||||||
"RegexExtract": "Regex Extract",
|
"RegexExtract": "Regex Extract",
|
||||||
"RegexReplace": "Regex Replace",
|
"RegexReplace": "Regex Replace",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user