{"id":"regex-classes","slug":"alternatives-and-classes","title":"Alternatives and character classes","text":"A pattern of plain characters matches only that exact text. Special characters let one pattern match many different texts. This step shows the ones that choose between alternatives and between characters.\n\n### Alternatives\n\nA vertical bar `|` means **or**: `r\"cat|dog\"` matches either `cat` or `dog`:\n\n```python\nimport re\n\nfor word in [\"hotdog\", \"category\", \"parrot\"]:\n    print(word, re.search(r\"cat|dog\", word) is not None)\n```\n\n```output\nhotdog True\ncategory True\nparrot False\n```\n\n### Character classes\n\nSquare brackets make a **character class**, which matches **one** character: any of those listed. `r\"[aeiou]\"` matches one vowel. A dash between two characters gives a range: `[0-9]` is any digit, `[a-z]` any small letter and `[A-Za-z]` any letter. A dash first or last in the brackets has nothing to make a range with, so it's an ordinary dash: `[a-z-]` is a small letter or a dash. A `^` right after the opening bracket turns the class around: `[^0-9]` is any character that is *not* a digit. Classes in a row match characters in a row, so `r\"[0-2][0-9]\"` matches two digits, the first of them 0, 1 or 2:\n\n```pycon\n>>> import re\n>>> re.findall(r\"[aeiou]\", \"regular expression\")\n['e', 'u', 'a', 'e', 'e', 'i', 'o']\n>>> re.findall(r\"[A-Z]\", \"Ada Lovelace met Charles Babbage\")\n['A', 'L', 'C', 'B']\n>>> re.findall(r\"[^a-z ]\", \"it's 9 a.m.!\")\n[\"'\", '9', '.', '.', '!']\n>>> re.findall(r\"[0-2][0-9]\", \"07 31 19 42 20\")\n['07', '19', '20']\n```\n\nSome classes are so common that they have a short form. `\\d` is a digit, the same as `[0-9]`. `\\w` is a \"word character\": a letter, a digit or the underscore `_`. `\\s` is whitespace: a space, a tab or a line break.\n\n### The dot, the start and the end\n\nA dot `.` matches any one character, except a line break. `^` matches at the start of the text and `$` at its end, without using up any character. With them you can check how a text starts or ends, or, with both, the whole text:\n\n```python\nimport re\n\nfor code in [\"A1\", \"B7x\", \"x9\", \"C3\"]:\n    if re.search(r\"^[A-Z][0-9]$\", code):\n        print(code, \"is a valid code\")\nprint(re.findall(r\"c.t\", \"cat, cot, coat, cut\"))\n```\n\n```output\nA1 is a valid code\nC3 is a valid code\n['cat', 'cot', 'cut']\n```\n\nWithout the `^` and the `$`, `B7x` would be valid too, because `B7` is in it. And `coat` isn't found by `c.t`, because the dot stands for exactly one character.\n\n### Special characters as themselves\n\nTo match one of the special characters `. ^ $ * + ? ( ) [ ] { } | \\` as an ordinary character, put a backslash before it: `r\"\\.\"` matches a dot and `r\"\\$\"` a dollar sign. Compare:\n\n```pycon\n>>> import re\n>>> re.findall(r\"\\d.\\d\", \"1.5 and 2x5\")\n['1.5', '2x5']\n>>> re.findall(r\"\\d\\.\\d\", \"1.5 and 2x5\")\n['1.5']\n```\n\nInside square brackets most special characters lose their meaning, so `[.?!]` matches a period, a question mark or an exclamation mark.\n","task":"Fill in the pattern so that the program finds the orders of tea or coffee. The program should print:\n\n```\nhot: green tea\nhot: black coffee\n```\n","kind":"fill","seed":"import re\n\norders = [\"green tea\", \"orange juice\", \"black coffee\", \"water\"]\nfor order in orders:\n    if re.search(r\"[[tea|coffee‖coffee|tea]]\", order):\n        print(\"hot:\", order)\n","solution":"import re\n\norders = [\"green tea\", \"orange juice\", \"black coffee\", \"water\"]\nfor order in orders:\n    if re.search(r\"tea|coffee\", order):\n        print(\"hot:\", order)\n","hints":["The pattern should match either the word `tea` or the word `coffee`.","The vertical bar means or: `tea|coffee`."],"tests":[{"name":"Output","stdin":"","expect":"hot: green tea\nhot: black coffee","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"import re\n\nprint(re.findall(r\"[0-9][a-z]\", \"3d 4K 5g 66 7up\"))\nprint(re.findall(r\"b.g\", \"big bag bug b g\"))\nprint(re.search(r\"^Hi\", \"Oh, Hi!\") is not None)\nprint(re.findall(r\"\\w\\w\", \"a ab abc\"))\n","solution":"import re\n\nprint(re.findall(r\"[0-9][a-z]\", \"3d 4K 5g 66 7up\"))\nprint(re.findall(r\"b.g\", \"big bag bug b g\"))\nprint(re.search(r\"^Hi\", \"Oh, Hi!\") is not None)\nprint(re.findall(r\"\\w\\w\", \"a ab abc\"))\n","hints":["The first pattern is a digit followed by a small letter. `K` is a capital, and `66` has no letter.","The dot matches any one character, a space too.","`^` means the start of the text. `\\w\\w` is two word characters in a row, and a match can't be used twice."],"tests":[{"name":"Output","stdin":"","expect":"['3d', '5g', '7u']\n['big', 'bag', 'bug', 'b g']\nFalse\n['ab', 'ab']","hidden":false}],"require":[],"forbid":[],"lines":["['3d', '5g', '7u']","['big', 'bag', 'bug', 'b g']","False","['ab', 'ab']"]},{"task":"A square on a chess board is named by a letter from `a` to `h` and a number from 1 to 8, such as `e4`. Write a function `is_chess_square(text)` that returns `True` if the whole text is such a name, and `False` otherwise. Use a regular expression. For example,\n\n```python\nprint(is_chess_square(\"e4\"))\nprint(is_chess_square(\"i9\"))\nprint(is_chess_square(\"a10\"))\n```\n\nshould print:\n\n```\nTrue\nFalse\nFalse\n```\n","kind":"code","seed":"import re\n\n\ndef is_chess_square(text):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(is_chess_square(\"e4\"))\n    print(is_chess_square(\"i9\"))\n    print(is_chess_square(\"a10\"))\n","solution":"import re\n\n\ndef is_chess_square(text):\n    return re.search(r\"^[a-h][1-8]$\", text) is not None\n\n\nif __name__ == \"__main__\":\n    print(is_chess_square(\"e4\"))\n    print(is_chess_square(\"i9\"))\n    print(is_chess_square(\"a10\"))\n","hints":["Two character classes in a row: a letter from a to h, then a digit from 1 to 8.","Put `^` before them and `$` after them, so that nothing else can come before or after: `r\"^[a-h][1-8]$\"`.","`re.search` returns a match or `None`. Return `... is not None` to get `True` or `False`."],"tests":[{"name":"Test 1","stdin":"","call":"print(is_chess_square(\"e4\"))\nprint(is_chess_square(\"i9\"))\nprint(is_chess_square(\"a10\"))\n","expect":"True\nFalse\nFalse","hidden":false},{"name":"Test 2","stdin":"","call":"print(is_chess_square(\"h8\"))\nprint(is_chess_square(\"a1\"))\nprint(is_chess_square(\"E4\"))\nprint(is_chess_square(\"b0\"))\nprint(is_chess_square(\"c\"))\nprint(is_chess_square(\"xd5\"))\n","expect":"True\nTrue\nFalse\nFalse\nFalse\nFalse","hidden":true}],"require":[{"pattern":"\\bre\\.","message":"Check the name with a regular expression from the `re` module."}],"forbid":[]}]}