Skip to content

Instantly share code, notes, and snippets.

@lucaspar
Last active June 23, 2022 23:51
Show Gist options
  • Save lucaspar/57efb9ea11c4d83919b5e8e92d1e15f5 to your computer and use it in GitHub Desktop.
Save lucaspar/57efb9ea11c4d83919b5e8e92d1e15f5 to your computer and use it in GitHub Desktop.
MAC address finder
"""Method to find MAC address in text (find_mac_address()) and testing routine."""
from dataclasses import dataclass
from typing import List, Optional, Set
import re
import random
RANDOM_SEED = 42
RND = random.Random(RANDOM_SEED)
def main():
"""Main function."""
print("QUICK TEST:")
test_find_mac_address(num_of_macs=20)
print("\nLONG TEST:")
test_find_mac_address()
print("TEST PASSED")
def find_mac_address(text: str) -> List[str]:
"""Finds MAC addresses in text.
It assumes MAC addresses separated by dashes or colons and it's of lower, upper, or mixed case:
xX:xX:XX:xx:XX:xX or XX-xX-XX-Xx-XX-XX.
Args:
text: text to find MAC addresses in.
"""
regex_matcher_str_colons = "([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})"
regex_matcher_str_dashes = "([0-9a-fA-F]{2}[-]){5}([0-9a-fA-F]{2})"
either_one = "|".join([regex_matcher_str_colons, regex_matcher_str_dashes])
regex_matcher = re.compile(either_one)
matches = regex_matcher.finditer(text)
list_all_matches = list()
for match in matches:
list_all_matches.append(match.group())
return list_all_matches
def test_find_mac_address(num_of_macs: int = 10_000) -> None:
"""Tests find_mac_address().
Args:
target_num_macs: number of MAC addresses to generate and find.
"""
# generate random text with mac addresses to be found:
possible_mac_separators = set(":-")
text_gen = RandomTextGenerator(symbols_to_exclude=possible_mac_separators)
set_of_macs_to_find = {
get_random_mac(separator="random", case="random") for _ in range(num_of_macs)
}
random_texts = {
text_gen.random_text(
min_length=200, max_length=300, newline_prob=1 / 50, space_prob=1 / 6
)
for _ in range(num_of_macs)
}
random_texts_with_mac = text_gen.random_text() + "".join(
[f"{mac}{text}" for mac, text in zip(set_of_macs_to_find, random_texts)]
)
macs_found = find_mac_address(random_texts_with_mac)
print_things = num_of_macs < 50
if print_things:
__indented_random_texts_with_mac = "\n".join(
["\t" + line for line in random_texts_with_mac.split("\n")]
)
print(
"RANDOM TEXT WITH MACS::\n\n{}\n".format(__indented_random_texts_with_mac)
)
# find MAC addresses in random texts:
print("MAC ADDRESSES FOUND:\n")
for mac in macs_found:
print("\t{}".format(mac))
print("")
# make sure all MAC addresses were found:
set_of_macs_found = set(macs_found)
found_but_not_expected = set_of_macs_found - set_of_macs_to_find
expected_but_not_found = set_of_macs_to_find - set_of_macs_found
mistakes = found_but_not_expected | expected_but_not_found
assert (
len(macs_found) == len(set_of_macs_to_find)
and set_of_macs_found == set_of_macs_to_find
), (
"Not all MAC addresses were found:\n"
+ "\tFound but not expected: {}".format(found_but_not_expected)
+ "\n"
+ "\tExpected but not found: {}".format(expected_but_not_found)
)
# if set is empty
if len(mistakes) == 0:
print("All {} MAC addresses were found.".format(len(set_of_macs_to_find)))
else:
raise ValueError("Not all MAC addresses were found: {}".format(mistakes))
def get_random_mac(separator: str = ":", case: str = "upper") -> str:
"""Generates a random MAC address."""
# argument checks
lowercase_values = {"lower", "lowercase"}
uppercase_values = {"upper", "uppercase"}
if isinstance(case, str):
assert case in lowercase_values | uppercase_values | {
"random"
}, "Invalid value for case: '{}'".format(case)
assert (
len(separator) == 1 or separator == "random"
), "Invalid separator: '{}'".format(separator)
if separator == "random":
separator = RND.choice(["-", ":"])
mac = [RND.randint(0x00, 0x7F) for _ in range(6)]
# format MAC address:
mac_address = separator.join(map("{:02x}".format, mac))
# set case
is_lowercase = RND.random() < 0.5 if case == "random" else case in lowercase_values
mac_address = mac_address.lower() if is_lowercase else mac_address.upper()
return mac_address
@dataclass
class RandomTextGenerator:
"""Helper methods for randomly generated text."""
_bag_of_chars: Optional[Set[str]] = None
_random_seed: int = RANDOM_SEED
include_lowercase: bool = True
include_numbers: bool = True
include_symbols: bool = True
include_uppercase: bool = True
symbols_to_exclude: Optional[Set] = None
def __post_init__(self):
self._bag_of_chars = self._generate_bag_of_characters()
self._rnd = random.Random(self._random_seed)
def _generate_bag_of_characters(self) -> Set[str]:
"""Generates a bag of characters."""
bag_of_chars = set()
if self.include_symbols:
bag_of_chars.update(set("!@#$%^&*()_+-=[]{}|;':,./<>?`~"))
if self.include_numbers:
bag_of_chars.update(set("0123456789"))
if self.include_lowercase:
bag_of_chars.update(set("abcdefghijklmnopqrstuvwxyz"))
if self.include_uppercase:
bag_of_chars.update(set("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))
if self.symbols_to_exclude is not None:
bag_of_chars -= self.symbols_to_exclude
assert len(bag_of_chars) > 0, "No characters to choose from."
return bag_of_chars
def random_text(
self,
min_length: int = 10,
max_length: int = 25,
space_prob: float = 0,
newline_prob: float = 0,
) -> str:
"""Generates a random text.
Args:
min_length: Minimum length of the text.
max_length: Maximum length of the text.
space_prob: Probability of a space character.
newline_prob: Probability of a newline character.
"""
max_length_override = max(min_length, max_length)
min_length_override = min(min_length, max_length)
target_length = self._rnd.randint(min_length_override, max_length_override)
assert 0 <= space_prob < 1, "Space probability is out of range: {}".format(
space_prob
)
assert 0 <= newline_prob < 1, "Newline probability is out of range: {}".format(
newline_prob
)
assert (
target_length >= min_length_override
and target_length <= max_length_override
), "Random length is out of range: {}".format(target_length)
assert self._bag_of_chars is not None, "Bag of characters is not initialized."
# define characters to choose from
random_text = ""
while len(random_text) < target_length:
# add single character
if self._rnd.random() < newline_prob:
random_text += "\n"
else:
random_text += self._rnd.choice(list(self._bag_of_chars))
assert (
len(random_text) == target_length
), "Length of random text is not correct: got {} instead of {}".format(
len(random_text), target_length
)
return random_text
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment