diff --git a/grep.json b/grep.json new file mode 100644 index 0000000000..f002afba4c --- /dev/null +++ b/grep.json @@ -0,0 +1,251 @@ +{ + "#": [ + "JSON doesn't allow for multi-line strings, so all outputs are presented ", + "here as arrays of strings. It's up to the test generator to join the ", + "lines together with line breaks.", + "", + "The tests are divided into two groups: ", + "* Grepping a single file", + "* Grepping multiple files at once", + "", + "The language track implementing this exercise should", + "ensure that when the tests run, three files are created", + "with the following contents. The file names and their", + "contents are listed in the 'files' section below." + ], + "files": [ + { + "name": "iliad.txt", + "contents": [ + "Achilles sing, O Goddess! Peleus' son;", + "His wrath pernicious, who ten thousand woes", + "Caused to Achaia's host, sent many a soul", + "Illustrious into Ades premature,", + "And Heroes gave (so stood the will of Jove)", + "To dogs and to all ravening fowls a prey,", + "When fierce dispute had separated once", + "The noble Chief Achilles from the son", + "Of Atreus, Agamemnon, King of men." + ] + }, + { + "name": "midsummer-night.txt", + "contents": [ + "I do entreat your grace to pardon me.", + "I know not by what power I am made bold,", + "Nor how it may concern my modesty,", + "In such a presence here to plead my thoughts;", + "But I beseech your grace that I may know", + "The worst that may befall me in this case,", + "If I refuse to wed Demetrius." + ] + }, + { + "name": "paradise-lost.txt", + "contents": [ + "Of Mans First Disobedience, and the Fruit", + "Of that Forbidden Tree, whose mortal tast", + "Brought Death into the World, and all our woe,", + "With loss of Eden, till one greater Man", + "Restore us, and regain the blissful Seat,", + "Sing Heav'nly Muse, that on the secret top", + "Of Oreb, or of Sinai, didst inspire", + "That Shepherd, who first taught the chosen Seed" + ] + } + ], + "single-file": { + "description": ["Test grepping a single file"], + "cases": [ + { + "description": "One file, one match, no flags", + "pattern": "Agamemnon", + "flags": [], + "files": ["iliad.txt"], + "expected": [ + "Of Atreus, Agamemnon, King of men." + ] + }, + { + "description": "One file, several matches, no flags", + "pattern": "may", + "flags": [], + "files": ["midsummer-night.txt"], + "expected": [ + "Nor how it may concern my modesty,", + "But I beseech your grace that I may know", + "The worst that may befall me in this case," + ] + }, + { + "description": "One file, several matches, print line numbers flag", + "pattern": "may", + "flags": ["-n"], + "files": ["midsummer-night.txt"], + "expected": [ + "3:Nor how it may concern my modesty,", + "5:But I beseech your grace that I may know", + "6:The worst that may befall me in this case," + ] + }, + { + "description": "One file, one match, print file names flag", + "pattern": "Forbidden", + "flags": ["-l"], + "files": ["paradise-lost.txt"], + "expected": [ + "paradise-lost.txt" + ] + }, + { + "description": "One file, several matches, case-insensitive flag", + "pattern": "ACHILLES", + "flags": ["-i"], + "files": ["iliad.txt"], + "expected": [ + "Achilles sing, O Goddess! Peleus' son;", + "The noble Chief Achilles from the son" + ] + }, + { + "description": "One file, several matches, inverted flag", + "pattern": "Of", + "flags": ["-v"], + "files": ["paradise-lost.txt"], + "expected": [ + "Brought Death into the World, and all our woe,", + "With loss of Eden, till one greater Man", + "Restore us, and regain the blissful Seat,", + "Sing Heav'nly Muse, that on the secret top", + "That Shepherd, who first taught the chosen Seed" + ] + }, + { + "description": "One file, one match, match entire lines flag", + "pattern": "With loss of Eden, till one greater Man", + "flags": ["-x"], + "files": ["paradise-lost.txt"], + "expected": [ + "With loss of Eden, till one greater Man" + ] + }, + { + "description": "One file, one match, multiple flags", + "pattern": "OF ATREUS, Agamemnon, KIng of MEN.", + "flags": ["-n", "-i", "-x"], + "files": ["iliad.txt"], + "expected": [ + "9:Of Atreus, Agamemnon, King of men." + ] + }, + { + "description": "One file, no matches, various flags", + "pattern": "Gandalf", + "flags": ["-n", "-l", "-x", "-i"], + "files": ["iliad.txt"], + "expected": [] + } + ] + }, + "multiple-files": { + "description": ["Test grepping multiples files at once"], + "cases": [ + { + "description": "Multiple files, one match, no flags", + "pattern": "Agamemnon", + "flags": [], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "iliad.txt:Of Atreus, Agamemnon, King of men." + ] + }, + { + "description": "Multiple files, several matches, no flags", + "pattern": "may", + "flags": [], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "midsummer-night.txt:Nor how it may concern my modesty,", + "midsummer-night.txt:But I beseech your grace that I may know", + "midsummer-night.txt:The worst that may befall me in this case," + ] + }, + { + "description": "Multiple files, several matches, print line numbers flag", + "pattern": "that", + "flags": ["-n"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "midsummer-night.txt:5:But I beseech your grace that I may know", + "midsummer-night.txt:6:The worst that may befall me in this case,", + "paradise-lost.txt:2:Of that Forbidden Tree, whose mortal tast", + "paradise-lost.txt:6:Sing Heav'nly Muse, that on the secret top" + ] + }, + { + "description": "Multiple files, one match, print file names flag", + "pattern": "who", + "flags": ["-l"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "iliad.txt", + "paradise-lost.txt" + ] + }, + { + "description": "Multiple files, several matches, case-insensitive flag", + "pattern": "TO", + "flags": ["-i"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "iliad.txt:Caused to Achaia's host, sent many a soul", + "iliad.txt:Illustrious into Ades premature,", + "iliad.txt:And Heroes gave (so stood the will of Jove)", + "iliad.txt:To dogs and to all ravening fowls a prey,", + "midsummer-night.txt:I do entreat your grace to pardon me.", + "midsummer-night.txt:In such a presence here to plead my thoughts;", + "midsummer-night.txt:If I refuse to wed Demetrius.", + "paradise-lost.txt:Brought Death into the World, and all our woe,", + "paradise-lost.txt:Restore us, and regain the blissful Seat,", + "paradise-lost.txt:Sing Heav'nly Muse, that on the secret top" + ] + }, + { + "description": "Multiple files, several matches, inverted flag", + "pattern": "a", + "flags": ["-v"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "iliad.txt:Achilles sing, O Goddess! Peleus' son;", + "iliad.txt:The noble Chief Achilles from the son", + "midsummer-night.txt:If I refuse to wed Demetrius." + ] + }, + { + "description": "Multiple files, one match, match entire lines flag", + "pattern": "But I beseech your grace that I may know", + "flags": ["-x"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "midsummer-night.txt:But I beseech your grace that I may know" + ] + }, + { + "description": "Multiple files, one match, multiple flags", + "pattern": "WITH LOSS OF EDEN, TILL ONE GREATER MAN", + "flags": ["-n", "-i", "-x"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [ + "paradise-lost.txt:4:With loss of Eden, till one greater Man" + ] + }, + { + "description": "Multiple files, no matches, various flags", + "pattern": "Frodo", + "flags": ["-n", "-l", "-x", "-i"], + "files": ["iliad.txt", "midsummer-night.txt", "paradise-lost.txt"], + "expected": [] + } + ] + } +} \ No newline at end of file diff --git a/grep.md b/grep.md index 266f4a28ea..37fa8bdb27 100644 --- a/grep.md +++ b/grep.md @@ -1,25 +1,60 @@ -Given a string `filename` and regular expression `pattern`, collect and -return the line numbers and contents of all lines in the file denoted by -`filename` that match `pattern`. - -In other words, solutions should open the file described by `filename` -and read each of its lines while keeping track of line numbers (starting at 1). -If a line matches `pattern`, create a pair containing the line's -number and the line itself. -After reading the entire file, return a list of all such pairs sorted -from lowest line number to highest. - -The name "grep" comes from the [Unix program](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) with the same name. -(In Unix, "grep" was a mnemonic for a useful command to another program [sed](http://www.gnu.org/software/sed/manual/sed.html), whose name means "stream editor".) - -The Unix `grep` can be run from the command line and accepts a variety of flags. -Try reproducing some of these in your implementation. For example: -- Accept a pattern and list of files on the command-line, print results - for each file to the console. -- Implement the flags: - - `-l` Print only the names of files that contain at least one matching line. - - `-v` Invert the program -- collect all lines that fail to match the pattern. - - `-x` Only match entire lines, instead of lines that contain a match. -- Support multiple flags at once. - For example, running `grep -l -x "hello" file1.txt file2.txt` should - print the names of files that do not contain the string "hello" +The Unix [`grep`](http://pubs.opengroup.org/onlinepubs/9699919799/utilities/grep.html) command can be used to search for lines in one or more files +that match a user-provided search query (known as the *pattern*). + +The `grep` command takes three arguments: + +1. The pattern used to match lines in a file. +2. Zero or more flags to customize the matching behavior. +3. One or more files in which to search for matching lines. + +Your task is to implement the `grep` function, which should read the contents +of the specified files, find the lines that match the specified pattern +and then output those lines as a single string. Note that the lines should +be output in the order in which they were found, with the first matching line +in the first file being output first. + +As an example, suppose there is a file named "input.txt" with the following contents: + +
+hello
+world
+hello again
+
+ +If we were to call `grep "hello" input.txt`, the returned string should be: + +
+hello
+hello again
+
+ +### Flags + +As said earlier, the `grep` command should also support the following flags: + +- `-n` Print the line numbers of each matching line. +- `-l` Print only the names of files that contain at least one matching line. +- `-i` Match line using a case-insensitive comparison. +- `-v` Invert the program -- collect all lines that fail to match the pattern. +- `-x` Only match entire lines, instead of lines that contain a match. + +If we run `grep -n "hello" input.txt`, the `-n` flag will require the matching +lines to be prefixed with its line number: + +
+1:hello
+3:hello again
+
+ +And if we run `grep -i "HELLO" input.txt`, we'll do a case-insensitive match, +and the output will be: + +
+hello
+hello again
+
+ +The `grep` command should support multiple flags at once. + +For example, running `grep -l -v "hello" file1.txt file2.txt` should +print the names of files that do not contain the string "hello". \ No newline at end of file