-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsimple-regex-tester.py
More file actions
97 lines (79 loc) · 2.93 KB
/
simple-regex-tester.py
File metadata and controls
97 lines (79 loc) · 2.93 KB
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
"""Test regular expressions interactively against text input."""
import re
import sys
import argparse
ANSI_MATCH = "\033[42;30m"
ANSI_GROUP = "\033[43;30m"
RESET = "\033[0m"
def highlight(text: str, pattern: re.Pattern, no_color: bool) -> str:
if no_color:
return text
result, last = [], 0
for m in pattern.finditer(text):
result.append(text[last : m.start()])
result.append(f"{ANSI_MATCH}{m.group(0)}{RESET}")
last = m.end()
result.append(text[last:])
return "".join(result)
def run(pattern: re.Pattern, text: str, mode: str, no_color: bool) -> None:
matches = list(pattern.finditer(text))
if mode == "match":
m = pattern.fullmatch(text.strip())
if m:
print(f"Full match: {m.group(0)!r}")
for i, g in enumerate(m.groups(), 1):
print(f" group {i}: {g!r}")
else:
print("No full match.")
return
if not matches:
print("No matches found.")
return
print(f"{len(matches)} match(es):\n")
for i, m in enumerate(matches, 1):
print(f" [{i}] pos {m.start()}-{m.end()} {m.group(0)!r}")
for j, g in enumerate(m.groups(), 1):
print(f" group {j}: {g!r}")
print(f"\nHighlighted:\n {highlight(text, pattern, no_color)}")
if mode == "split":
parts = pattern.split(text)
print(f"\nSplit ({len(parts)} parts):")
for p in parts:
print(f" {p!r}")
if mode == "sub":
print("\nReplacement: provide --replace to use sub mode.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Test regex patterns against text.")
parser.add_argument("pattern", help="Regular expression pattern")
parser.add_argument(
"text", nargs="?", help="Text to match (reads stdin if omitted)"
)
parser.add_argument(
"-m", "--mode", choices=["search", "match", "split"], default="search"
)
parser.add_argument(
"-r", "--replace", help="Replace matches with this string (activates sub mode)"
)
parser.add_argument("-i", "--ignore-case", action="store_true")
parser.add_argument("-M", "--multiline", action="store_true")
parser.add_argument("-s", "--dotall", action="store_true")
parser.add_argument("--no-color", action="store_true")
args = parser.parse_args()
text = args.text if args.text is not None else sys.stdin.read()
flags = 0
if args.ignore_case:
flags |= re.IGNORECASE
if args.multiline:
flags |= re.MULTILINE
if args.dotall:
flags |= re.DOTALL
try:
compiled = re.compile(args.pattern, flags)
except re.error as e:
print(f"Invalid pattern: {e}", file=sys.stderr)
sys.exit(1)
if args.replace is not None:
result, count = compiled.subn(args.replace, text)
print(f"{count} substitution(s):\n\n{result}")
else:
run(compiled, text, args.mode, args.no_color)