<jupyter_start><jupyter_text>Table of Content- [2.4 Matching and Searching for Text Patterns](2.4)- [2.7 Specifying a Regular Expression for the Shortest Match](2.7)- [2.8 Writing a Regular Expression for Multiline Patterns](2.8)- [2.14 Combining and Concatenating Strings](2.14) --- 2.4 Matching and Searching for Text Patterns Solution<jupyter_code>import re

t1 = '12/25/2016'
t2 = 'Dec 25, 2016'
print(bool(re.match(r'\d+/\d+/\d+', t1)))
print(bool(re.match(r'\d+/\d+/\d+', t2)))<jupyter_output>True
False<jupyter_text>- If the same pattern is to be performd several times, it usually pays to precompile it.<jupyter_code>datepat = re.compile(r'\d+/\d+/\d+')

print(bool(datepat.match(t1)))
print(bool(datepat.match(t2)))<jupyter_output>True
False<jupyter_text>`match` returns only the first occurence.  If all occurences are needed, `findall()` should be used.<jupyter_code>t3 = '12/25/2016 ------ 12/26/2016 ----- 12/27/2017'

datepat.findall(t3)<jupyter_output><empty_output><jupyter_text>- Matching iteratively: `finditer`<jupyter_code>for m in datepat.finditer(t3):
    print(m.group())<jupyter_output>12/25/2016
12/26/2016
12/27/2017<jupyter_text>- Capteruring re groups<jupyter_code>datepat = re.compile(r'(\d+)/(\d+)/(\d+)')

m = datepat.match(t1)
print(m.group(0))
print(m.group(1))
print(m.group(2))
print(m.group(3))
print(m.groups())<jupyter_output>12/25/2016
12
25
2016
('12', '25', '2016')<jupyter_text>---  2.7 Specifying a Regular Expression for the Shortest Match<jupyter_code>pat = re.compile(r'\"(.*)\"')

s1 = '"First Word" "Second Word"'
pat.findall(s1)<jupyter_output><empty_output><jupyter_text>Solution<jupyter_code>pat_non_greedy = re.compile(r'\"(.*?)\"')

pat_non_greedy.findall(s1)<jupyter_output><empty_output><jupyter_text>- \* operator is greedy.      - Add the ? modifier after the \* operator to make it non-greedy --- 2.8 Writing a Regular Expression for Multiline Patterns<jupyter_code>pat = re.compile(r'/\*(.*?)\*/')

t1 = "/* 123 \n 456 */"
pat.findall(t1)<jupyter_output><empty_output><jupyter_text>Solution<jupyter_code>pat_non_capture_group = re.compile(r'/\*((?:.|\n)*?)\*/')
pat_non_capture_group.findall(t1)<jupyter_output><empty_output><jupyter_text>- ***?:*** is used to sepcify non-capture group    - (i.e. a group for the purpose of matching, but not being captured) --- 2.14 Combining and Concatenating Strings Discussion- Using + to join a lot of strings is inefficient and not recommanded- Sometimes concatenation is not necessary<jupyter_code>a, b, c= "123"

print(a+":"+b+":"+c)          # Worst
print(':'.join([a, b, c]))    # Still bad

print(a, b, c, sep=':')       # Better<jupyter_output>1:2:3
1:2:3
1:2:3
{'fraction_non_alphanumeric': 0.17854463615903976, 'fraction_numerical': 0.056639159789947484, 'mean_word_length': 5.216783216783217, 'pattern_counts': {'":': 2, '<': 36, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '586032', 'license': 'MIT', 'n_tokens_mistral': 1146, 'n_tokens_neox': 1040, 'n_words': 221, 'path': 'Ch02 - Strings and Text.ipynb', 'repo_name': 'Lee-W/Python_Cookbook', 'size': '2666'}