""" AUTHOR: Khushal P Soonderji DATE: Sunday, 28th Apr., 2024 OBJECTIVE: To provide a convenient way to perform RegEx operations like finding patterns and substituting them. REFERENCES: 1) https://www.w3schools.com/python/python_regex.asp DOWNLOADS: N/A """ # ***************************************************************************************************************** # ***** **** # *** IMPORT *** # ***** **** # ***************************************************************************************************************** # To make sibling directories accessible for imports: import sys sys.path.append(".") sys.path.append("..") # To work with RegEx: import re # ***************************************************************************************************************** # ***** **** # *** MACROS / ONE-TIME INIT *** # ***** **** # ***************************************************************************************************************** # Common RegEx patterns: REGEX_EMAIL_ID = r"[\d\w_.+]*@[\d\w_]*.[\d\w]{2,}" REGEX_PASSWORD = r"^(?=.*[a-z])(?=.*[A-Z])(?=.*[\d])(?=.*[!@#$%^&*()_+{}\[\]:;<>,.?~\\\/-]).{8,}$" REGEX_NAME = r"^[\d\w .\-]{1,30}$" REGEX_USERNAME = r"^[\d\w_]{8,25}$" REGEX_CONTACT_NUMBER = r"\+?\d{0,3}\s*\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}" REGEX_DATE = r"\b(?:\d{4}-\d{2}-\d{2}|(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2},?\s+\d{4}|\d{1,2}\/\d{1,2}\/\d{4}|\d{1,2}-\d{1,2}-\d{2}|\d{1,2}(?:st|nd|rd|th)\s+(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?),?\s+\d{4})\b" REGEX_GSTIN = r"[0-9]{2}[A-Z]{5}[0-9]{4}[A-Z]{1}[1-9A-Z]{1}Z[0-9A-Z]{1}" REGEX_PAN = r"[A-Z]{5}[0-9]{4}[A-Z]{1}" REGEX_IPV4 = (r"[0-9]{1,3}\." * 3) + r"[0-9]{1,3}" REGEX_IPV6 = (r"[0-9a-fA-F]{1,4}:" * 7) + r"[0-9a-fA-F]{1,4}" REGEX_IFSC = r"[A-Z]{4}0[A-Z0-9]{6}" REGEX_UPI = r"[a-zA-Z0-9\.\-]{2,256}@[a-zA-Z][a-zA-Z]{2,64}" REGEX_MAC_ADDRESS = r"([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})|([0-9a-fA-F]{4}\\.[0-9a-fA-F]{4}\\.[0-9a-fA-F]{4})" REGEX_METRIC_WEIGHT = r"[\d\.]+[ ]?[k]?g" # RegEx chars (append them to the patterns if needed): REGEX_START = "^" REGEX_END = "$" # ***************************************************************************************************************** # ***** **** # *** VARIABLES *** # ***** **** # ***************************************************************************************************************** # --- Nothing Yet # ***************************************************************************************************************** # ***** **** # *** FUNCTIONS *** # ***** **** # ***************************************************************************************************************** def find(text, pattern, case_sensitive = True, dot_all = False): """ Returns a list of substrings that match the given RegEx pattern in the input text. :param text: The text in which the pattern needs to be found. :param pattern: The RegEx pattern to look for. :param case_sensitive: Whether, or not, you want the operation to be case-sensitive. :param dot_all: Allow all characters to be matched in ".". :return: An array (list) of substring that match the pattern. Can be an empty list as well. """ # Prepare the flags: flags = 0 if not case_sensitive: flags |= re.IGNORECASE if dot_all: flags |= re.DOTALL # Perform the RegEx operation, and clean the results: matches = [match if type(match) is str else match[1] for match in re.findall(pattern, text, flags = flags)] matches = [match for match in matches if len(match) > 0] # Return the results: return matches # --------------------------------------------------------------------------------------------------------------------- def find_first(text, pattern, case_sensitive = True, dot_all = False): """ Returns the first substring that matches the given RegEx pattern in the input text. :param text: The text in which the pattern needs to be found. :param pattern: The RegEx pattern to look for. :param case_sensitive: Whether, or not, you want the operation to be case-sensitive. :param dot_all: Allow all characters to be matched in ".". :return: The first match as a string, or None if no match was found.. """ matches = find( text = text, pattern = pattern, case_sensitive = case_sensitive, dot_all = dot_all ) if not matches: return None else: return matches[0] # --------------------------------------------------------------------------------------------------------------------- def replace(text, pattern, substitute_text, case_sensitive = True, dot_all = False): """ Replaces any substring in the text that matches the RegEx pattern. :param text: The text in which the substitutions need to be made. :param pattern: The RegEx pattern that needs to be substituted. :param substitute_text: The text that will replace the matches that were found. :param case_sensitive: Whether, or not, you want the operation to be case-sensitive. :param dot_all: Allow all characters to be matched in ".". :return: The text with the substitutions. If no matches are found, the original string is returned. """ # Prepare the flags: flags = 0 if not case_sensitive: flags |= re.IGNORECASE if dot_all: flags |= re.DOTALL # Perform the RegEx operation, and return the results: return re.sub(pattern, substitute_text, text, flags = flags) # --------------------------------------------------------------------------------------------------------------------- def search(text, pattern, case_sensitive = True, dot_all = False): """ Checks if the given RegEx pattern occurs ANYWHERE in the text that was provided. :param text: The text that needs to be matched against the pattern. :param pattern: The RegEx pattern to look for. :param case_sensitive: Whether, or not, you want the operation to be case-sensitive. :param dot_all: Allow all characters to be matched in ".". :return: True if the pattern matches, else False. """ # Prepare the flags: flags = 0 if not case_sensitive: flags |= re.IGNORECASE if dot_all: flags |= re.DOTALL # Perform the RegEx operation, and return the results: if re.search(pattern, text, flags = flags): return True else: return False # --------------------------------------------------------------------------------------------------------------------- def match(text, pattern, case_sensitive = True, dot_all = False): """ Checks if the given text matches the RegEx pattern that was provided. The check is made only at the start of the input string. :param text: The text that needs to be matched against the pattern. :param pattern: The RegEx pattern to look for. :param case_sensitive: Whether, or not, you want the operation to be case-sensitive. :param dot_all: Allow all characters to be matched in ".". :return: True if the pattern matches, else False. """ # Prepare the flags: flags = 0 if not case_sensitive: flags |= re.IGNORECASE if dot_all: flags |= re.DOTALL # Perform the RegEx operation, and return the results: if re.match(pattern, text, flags = flags): return True else: return False # --------------------------------------------------------------------------------------------------------------------- def split(text, pattern, case_sensitive = True, dot_all = False): """ Splits an input string based on the pattern that is being matched. :param text: The text that needs to be matched against the pattern. :param pattern: The RegEx pattern to look for. :param case_sensitive: Whether, or not, you want the operation to be case-sensitive. :param dot_all: Allow all characters to be matched in ".". :return: True if the pattern matches, else False. """ # Prepare the flags: flags = 0 if not case_sensitive: flags |= re.IGNORECASE if dot_all: flags |= re.DOTALL # Perform the RegEx operation, and return the results: substrings = re.split(pattern, text, flags = flags) if len(substrings) > 0 and substrings[0] == "": substrings.pop(0) return substrings # --------------------------------------------------------------------------------------------------------------------- def to_json(text, pattern, case_sensitive = True, dot_all = False): """ Gives out a dict from the extracted features in a string. It is based on the concept of Named Groups. Consider the following example (assuming the search is case-insensitive): TEXT: "UPI/309258561479/14:17:35/UPI/omsainurses@okhdfc" PATTERN: "upi/.*/(?P