{"id":"useful-techniques-regex","slug":"regular-expressions","title":"Regular expressions: matches, | and groups","text":"Is `KTX-0415` a booking code of the right form? Is `9021O` a postcode, or a typo with the letter O in it? Checking the form of a string with `charAt` and loops takes many lines. A **regular expression**, or **regex** for short, describes a form in one short String, and String's method `matches` checks whether a string has that form.\n\n### matches\n\n```java\nSystem.out.println(\"yes\".matches(\"yes\"));\nSystem.out.println(\"Yes\".matches(\"yes\"));\nSystem.out.println(\"yes!\".matches(\"yes\"));\n```\n\n```output\ntrue\nfalse\nfalse\n```\n\n- Letters and digits in a regex stand for themselves, so the regex `yes` describes exactly the text yes.\n- Upper and lower case differ, as they do for `equals`.\n- The **whole** string must fit the regex: `yes!` has a `!` left over.\n\nSo far, that's what `equals` does. The rest of this step and the next are what `equals` can't do.\n\n### Alternation: |\n\nA `|` means \"or\". The regex `yes|y|ok` describes three texts, and a string matches if it's any one of them:\n\n```java\nString[] answers = {\"yes\", \"y\", \"ok\", \"okay\", \"no\"};\nfor (String answer : answers) {\n    System.out.println(answer + \": \" + answer.matches(\"yes|y|ok\"));\n}\n```\n\n```output\nyes: true\ny: true\nok: true\nokay: false\nno: false\n```\n\n`okay` begins with `ok`, but the whole string has to fit one of the three.\n\n### Groups: ( )\n\nA `|` splits the whole regex in two. A ferry sells four kinds of tickets: `adult single`, `adult return`, `child single` and `child return`. The regex `adult|child single|return` would mean three choices: `adult`, `child single` or `return`. Parentheses make a **group**, and a `|` inside a group chooses only there:\n\n```java\nString ticket = \"(adult|child) (single|return)\";\nSystem.out.println(\"child return\".matches(ticket));\nSystem.out.println(\"adult single\".matches(ticket));\nSystem.out.println(\"adult\".matches(ticket));\nSystem.out.println(\"senior single\".matches(ticket));\n```\n\n```output\ntrue\ntrue\nfalse\nfalse\n```\n\nThe regex describes four texts: one of the words in the first group, a space, and one of the words in the second group. A group can be a part of a word, too: `gr(a|e)y` matches both gray and grey.\n\n```java\nSystem.out.println(\"grey\".matches(\"gr(a|e)y\") + \" \" + \"gray\".matches(\"gr(a|e)y\") + \" \" + \"groy\".matches(\"gr(a|e)y\"));\n```\n\n```output\ntrue true false\n```\n\n### Characters with a meaning of their own\n\n`|`, `(` and `)` aren't matched as themselves: they're part of the regex's own language. So are a few more characters, `. * + ? [ { ^ $ \\`, and also `]` and `}` when they close a `[` or a `{`. Step 4 gives most of them their meaning. (`^` and `$` mark the start and the end of the text, which `matches` checks anyway.) Letters, digits, spaces, dashes and colons are plain characters. To match a special character as itself, put `\\\\` in front of it in the Java String: in a regex, a backslash takes the special meaning away, and in a Java String, `\\\\` is how you write one backslash.\n\n```java\nSystem.out.println(\"(1)\".matches(\"\\\\(1\\\\)\"));\n```\n\n```output\ntrue\n```\n","task":"An ice cream stand sells four things: `single cone`, `single cup`, `double cone` and `double cup`. Fill in the regex, with two groups, so that it describes exactly those four. The program then prints:\n\n```\nsingle cone: true\ndouble cup: true\nsingle cup: true\ndouble cone: true\ntriple cone: false\ndouble: false\nsingle cones: false\n```\n","kind":"fill","seed":"public class Main {\n    public static void main(String[] args) {\n        String menu = \"[[(single|double) (cone|cup)‖(double|single) (cone|cup)‖(single|double) (cup|cone)‖(double|single) (cup|cone)]]\";\n        String[] orders = {\"single cone\", \"double cup\", \"single cup\", \"double cone\", \"triple cone\", \"double\", \"single cones\"};\n        for (String order : orders) {\n            System.out.println(order + \": \" + order.matches(menu));\n        }\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        String menu = \"(single|double) (cone|cup)\";\n        String[] orders = {\"single cone\", \"double cup\", \"single cup\", \"double cone\", \"triple cone\", \"double\", \"single cones\"};\n        for (String order : orders) {\n            System.out.println(order + \": \" + order.matches(menu));\n        }\n    }\n}\n","hints":["Like the ferry's tickets: a group with the two sizes, a space, and a group with the two ways to serve it.","`(single|double) (cone|cup)`"],"tests":[{"name":"Output","stdin":"","expect":"single cone: true\ndouble cup: true\nsingle cup: true\ndouble cone: true\ntriple cone: false\ndouble: false\nsingle cones: false","hidden":false}],"require":[],"forbid":[],"more":[{"task":"What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(\"dog\".matches(\"cat|dog food\"));\n        System.out.println(\"dog food\".matches(\"(cat|dog) food\"));\n        System.out.println(\"grey\".matches(\"gr(a|e)y\"));\n        System.out.println(\"Gray\".matches(\"gr(a|e)y\"));\n        System.out.println(\"cat food\".matches(\"cat|dog food\"));\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(\"dog\".matches(\"cat|dog food\"));\n        System.out.println(\"dog food\".matches(\"(cat|dog) food\"));\n        System.out.println(\"grey\".matches(\"gr(a|e)y\"));\n        System.out.println(\"Gray\".matches(\"gr(a|e)y\"));\n        System.out.println(\"cat food\".matches(\"cat|dog food\"));\n    }\n}\n","hints":["Without parentheses, `|` splits the whole regex: `cat|dog food` means `cat`, or `dog food`.","`dog` and `cat food` are neither of those two. In the fourth line, the capital G doesn't match the regex's small g."],"tests":[{"name":"Output","stdin":"","expect":"false\ntrue\ntrue\nfalse\nfalse","hidden":false}],"require":[],"forbid":[],"lines":["false","true","true","false","false"]},{"task":"The text UI of a notes app, like the ones in module 25, understands six commands: `add note`, `add task`, `remove note`, `remove task`, `list` and `quit`. Write the method `isCommand(String line)`, which returns whether a line is one of them, with a single call to `matches`. The check runs:\n\n```java\nSystem.out.println(isCommand(\"add task\"));\nSystem.out.println(isCommand(\"list\"));\nSystem.out.println(isCommand(\"list task\"));\nSystem.out.println(isCommand(\"add\"));\n```\n\nand expects:\n\n```\ntrue\ntrue\nfalse\nfalse\n```\n","kind":"code","seed":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(isCommand(\"add task\"));\n        System.out.println(isCommand(\"list\"));\n        System.out.println(isCommand(\"list task\"));\n        System.out.println(isCommand(\"add\"));\n    }\n\n    public static boolean isCommand(String line) {\n        return false;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        System.out.println(isCommand(\"add task\"));\n        System.out.println(isCommand(\"list\"));\n        System.out.println(isCommand(\"list task\"));\n        System.out.println(isCommand(\"add\"));\n    }\n\n    public static boolean isCommand(String line) {\n        return line.matches(\"(add|remove) (note|task)|list|quit\");\n    }\n}\n","hints":["Four of the commands are two words: `add` or `remove`, a space, and `note` or `task`. That part is two groups, as for the ferry's tickets. The other two commands are single words.","Put the three choices together with `|` outside the groups: `(add|remove) (note|task)|list|quit`."],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(isCommand(\"add task\"));\nSystem.out.println(isCommand(\"list\"));\nSystem.out.println(isCommand(\"list task\"));\nSystem.out.println(isCommand(\"add\"));\n","expect":"true\ntrue\nfalse\nfalse","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(isCommand(\"add note\") + \" \" + isCommand(\"remove note\") + \" \" + isCommand(\"remove task\") + \" \" + isCommand(\"quit\"));\nSystem.out.println(isCommand(\"Quit\") + \" \" + isCommand(\"add  note\") + \" \" + isCommand(\"add notes\") + \" \" + isCommand(\"\"));\n","expect":"true true true true\nfalse false false false","hidden":true},{"name":"Test 3","stdin":"","call":"System.out.println(isCommand(\"note\") + \" \" + isCommand(\"remove\") + \" \" + isCommand(\"list note\") + \" \" + isCommand(\"add task quit\"));\nSystem.out.println(isCommand(\"quit \") + \" \" + isCommand(\"addnote\") + \" \" + isCommand(\"remove list\"));\n","expect":"false false false false\nfalse false false","hidden":true}],"require":[{"pattern":"\\.matches\\s*\\(","min":1,"max":1,"message":"Check the line with one call to `matches`, with a regex that describes all six commands."}],"forbid":[{"pattern":"\\.equals\\s*\\(","message":"Let the regex do the comparing: no `equals` this time."}]}]}