瀏覽代碼

grep: add exercise (#1429)

* grep: WIP add exercise

* grep: implement exercise

* grep: add to config.json

* grep: update config.json and add hints

* grep: Add .keep file

* grep: improve reference solution

* grep: Make sure settings.gradle is in alphabetical order
FridaTveit 6 年之前
父節點
當前提交
2754bec3ae

+ 14
- 0
config.json 查看文件

@@ -640,6 +640,20 @@
640 640
     },
641 641
     {
642 642
       "core": false,
643
+      "difficulty": 5,
644
+      "slug": "grep",
645
+      "topics": [
646
+        "pattern_matching",
647
+        "strings",
648
+        "files",
649
+        "filtering",
650
+        "searching"
651
+      ],
652
+      "unlocked_by": "robot-name",
653
+      "uuid": "9c15ddab-7b52-43d4-b38c-0bf635b363c3"
654
+    },
655
+    {
656
+      "core": false,
643 657
       "difficulty": 6,
644 658
       "slug": "alphametics",
645 659
       "topics": [

+ 58
- 0
exercises/grep/.meta/hints.md 查看文件

@@ -0,0 +1,58 @@
1
+Since this exercise has difficulty 5 it doesn't come with any starter implementation.
2
+This is so that you get to practice creating classes and methods which is an important part of programming in Java.
3
+It does mean that when you first try to run the tests, they won't compile.
4
+They will give you an error similar to:
5
+```
6
+ path-to-exercism-dir\exercism\java\name-of-exercise\src\test\java\ExerciseClassNameTest.java:14: error: cannot find symbol
7
+        ExerciseClassName exerciseClassName = new ExerciseClassName();
8
+        ^
9
+ symbol:   class ExerciseClassName
10
+ location: class ExerciseClassNameTest
11
+```
12
+This error occurs because the test refers to a class that hasn't been created yet (`ExerciseClassName`).
13
+To resolve the error you need to add a file matching the class name in the error to the `src/main/java` directory.
14
+For example, for the error above you would add a file called `ExerciseClassName.java`.
15
+
16
+When you try to run the tests again you will get slightly different errors.
17
+You might get an error similar to:
18
+```
19
+  constructor ExerciseClassName in class ExerciseClassName cannot be applied to given types;
20
+        ExerciseClassName exerciseClassName = new ExerciseClassName("some argument");
21
+                                              ^
22
+  required: no arguments
23
+  found: String
24
+  reason: actual and formal argument lists differ in length
25
+```
26
+This error means that you need to add a [constructor](https://docs.oracle.com/javase/tutorial/java/javaOO/constructors.html) to your new class.
27
+If you don't add a constructor, Java will add a default one for you.
28
+This default constructor takes no arguments.
29
+So if the tests expect your class to have a constructor which takes arguments, then you need to create this constructor yourself.
30
+In the example above you could add:
31
+```
32
+ExerciseClassName(String input) {
33
+
34
+}
35
+``` 
36
+That should make the error go away, though you might need to add some more code to your constructor to make the test pass!
37
+
38
+You might also get an error similar to:
39
+```
40
+  error: cannot find symbol
41
+        assertEquals(expectedOutput, exerciseClassName.someMethod());
42
+                                                       ^
43
+  symbol:   method someMethod()
44
+  location: variable exerciseClassName of type ExerciseClassName
45
+```
46
+This error means that you need to add a method called `someMethod` to your new class.
47
+In the example above you would add:
48
+```
49
+String someMethod() {
50
+  return "";
51
+}
52
+```
53
+Make sure the return type matches what the test is expecting.
54
+You can find out which return type it should have by looking at the type of object it's being compared to in the tests.
55
+Or you could set your method to return some random type (e.g. `void`), and run the tests again.
56
+The new error should tell you which type it's expecting.
57
+
58
+After having resolved these errors you should be ready to start making the tests pass!

+ 65
- 0
exercises/grep/.meta/src/reference/java/GrepTool.java 查看文件

@@ -0,0 +1,65 @@
1
+import java.io.IOException;
2
+import java.nio.charset.Charset;
3
+import java.nio.file.Files;
4
+import java.nio.file.Paths;
5
+import java.util.Collections;
6
+import java.util.List;
7
+import java.util.stream.Collectors;
8
+import java.util.stream.IntStream;
9
+
10
+class GrepTool {
11
+
12
+    String grep(String pattern, List<String> flags, List<String> files) {
13
+        boolean shouldAddFilenameToEachLine = files.size() > 1;
14
+        return files
15
+            .stream()
16
+            .map(file -> grepFile(pattern, flags, file, shouldAddFilenameToEachLine))
17
+            .filter(line -> !line.isEmpty())
18
+            .collect(Collectors.joining("\n"));
19
+    }
20
+
21
+    private String grepFile(String pattern, List<String> flags, String filename, boolean shouldAddFilenameToEachLine) {
22
+        List<String> lines = readFile(filename);
23
+
24
+        String matchingLines = IntStream.range(0, lines.size())
25
+            .filter(lineIndex -> lineMatchesPattern(lines.get(lineIndex), pattern, flags))
26
+            .mapToObj(lineIndex -> {
27
+                String lineWithFlagsApplied = applyFlagsToLine(lines.get(lineIndex), flags, lineIndex + 1);
28
+                return shouldAddFilenameToEachLine ? filename + ":" + lineWithFlagsApplied : lineWithFlagsApplied;
29
+            })
30
+            .collect(Collectors.joining("\n"));
31
+
32
+        boolean shouldPrintFilenameInsteadOfMatchingLines = flags.contains("-l");
33
+        return shouldPrintFilenameInsteadOfMatchingLines && !matchingLines.isEmpty() ? filename : matchingLines;
34
+    }
35
+
36
+    private boolean lineMatchesPattern(String line, String pattern, List<String> flags) {
37
+        boolean isCaseInsensitive = flags.contains("-i");
38
+        String lineToCompare = isCaseInsensitive ? line.toLowerCase() : line;
39
+        String patternToCompare = isCaseInsensitive ? pattern.toLowerCase() : pattern;
40
+
41
+        boolean shouldMatchEntireLine = flags.contains("-x");
42
+        boolean matchesPattern = shouldMatchEntireLine
43
+            ? lineToCompare.equals(patternToCompare)
44
+            : lineToCompare.contains(patternToCompare);
45
+
46
+        boolean shouldInvertMatch = flags.contains("-v");
47
+        //noinspection SimplifiableConditionalExpression
48
+        return shouldInvertMatch ? !matchesPattern : matchesPattern;
49
+    }
50
+
51
+
52
+    private String applyFlagsToLine(String line, List<String> flags, int lineNumber) {
53
+        boolean shouldAddLineNumber = flags.contains("-n");
54
+        return shouldAddLineNumber ? lineNumber + ":" + line : line;
55
+    }
56
+
57
+    private List<String> readFile(String filename) {
58
+        try {
59
+            return Files.readAllLines(Paths.get(filename), Charset.forName("UTF-8"));
60
+        } catch (IOException e) {
61
+            return Collections.emptyList();
62
+        }
63
+    }
64
+
65
+}

+ 1
- 0
exercises/grep/.meta/version 查看文件

@@ -0,0 +1 @@
1
+1.1.0

+ 145
- 0
exercises/grep/README.md 查看文件

@@ -0,0 +1,145 @@
1
+# Grep
2
+
3
+Search a file for lines matching a regular expression pattern. Return the line
4
+number and contents of each matching line.
5
+
6
+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
7
+that match a user-provided search query (known as the *pattern*).
8
+
9
+The `grep` command takes three arguments:
10
+
11
+1. The pattern used to match lines in a file.
12
+2. Zero or more flags to customize the matching behavior.
13
+3. One or more files in which to search for matching lines.
14
+
15
+Your task is to implement the `grep` function, which should read the contents
16
+of the specified files, find the lines that match the specified pattern
17
+and then output those lines as a single string. Note that the lines should
18
+be output in the order in which they were found, with the first matching line
19
+in the first file being output first.
20
+
21
+As an example, suppose there is a file named "input.txt" with the following contents:
22
+
23
+```text
24
+hello
25
+world
26
+hello again
27
+```
28
+
29
+If we were to call `grep "hello" input.txt`, the returned string should be:
30
+
31
+```text
32
+hello
33
+hello again
34
+```
35
+
36
+### Flags
37
+
38
+As said earlier, the `grep` command should also support the following flags:
39
+
40
+- `-n` Print the line numbers of each matching line.
41
+- `-l` Print only the names of files that contain at least one matching line.
42
+- `-i` Match line using a case-insensitive comparison.
43
+- `-v` Invert the program -- collect all lines that fail to match the pattern.
44
+- `-x` Only match entire lines, instead of lines that contain a match.
45
+
46
+If we run `grep -n "hello" input.txt`, the `-n` flag will require the matching
47
+lines to be prefixed with its line number:
48
+
49
+```text
50
+1:hello
51
+3:hello again
52
+```
53
+
54
+And if we run `grep -i "HELLO" input.txt`, we'll do a case-insensitive match,
55
+and the output will be:
56
+
57
+```text
58
+hello
59
+hello again
60
+```
61
+
62
+The `grep` command should support multiple flags at once.
63
+
64
+For example, running `grep -l -v "hello" file1.txt file2.txt` should
65
+print the names of files that do not contain the string "hello".
66
+
67
+# Java Tips
68
+
69
+Since this exercise has difficulty 5 it doesn't come with any starter implementation.
70
+This is so that you get to practice creating classes and methods which is an important part of programming in Java.
71
+It does mean that when you first try to run the tests, they won't compile.
72
+They will give you an error similar to:
73
+```
74
+ path-to-exercism-dir\exercism\java\name-of-exercise\src\test\java\ExerciseClassNameTest.java:14: error: cannot find symbol
75
+        ExerciseClassName exerciseClassName = new ExerciseClassName();
76
+        ^
77
+ symbol:   class ExerciseClassName
78
+ location: class ExerciseClassNameTest
79
+```
80
+This error occurs because the test refers to a class that hasn't been created yet (`ExerciseClassName`).
81
+To resolve the error you need to add a file matching the class name in the error to the `src/main/java` directory.
82
+For example, for the error above you would add a file called `ExerciseClassName.java`.
83
+
84
+When you try to run the tests again you will get slightly different errors.
85
+You might get an error similar to:
86
+```
87
+  constructor ExerciseClassName in class ExerciseClassName cannot be applied to given types;
88
+        ExerciseClassName exerciseClassName = new ExerciseClassName("some argument");
89
+                                              ^
90
+  required: no arguments
91
+  found: String
92
+  reason: actual and formal argument lists differ in length
93
+```
94
+This error means that you need to add a [constructor](https://docs.oracle.com/javase/tutorial/java/javaOO/constructors.html) to your new class.
95
+If you don't add a constructor, Java will add a default one for you.
96
+This default constructor takes no arguments.
97
+So if the tests expect your class to have a constructor which takes arguments, then you need to create this constructor yourself.
98
+In the example above you could add:
99
+```
100
+ExerciseClassName(String input) {
101
+
102
+}
103
+``` 
104
+That should make the error go away, though you might need to add some more code to your constructor to make the test pass!
105
+
106
+You might also get an error similar to:
107
+```
108
+  error: cannot find symbol
109
+        assertEquals(expectedOutput, exerciseClassName.someMethod());
110
+                                                       ^
111
+  symbol:   method someMethod()
112
+  location: variable exerciseClassName of type ExerciseClassName
113
+```
114
+This error means that you need to add a method called `someMethod` to your new class.
115
+In the example above you would add:
116
+```
117
+String someMethod() {
118
+  return "";
119
+}
120
+```
121
+Make sure the return type matches what the test is expecting.
122
+You can find out which return type it should have by looking at the type of object it's being compared to in the tests.
123
+Or you could set your method to return some random type (e.g. `void`), and run the tests again.
124
+The new error should tell you which type it's expecting.
125
+
126
+After having resolved these errors you should be ready to start making the tests pass!
127
+
128
+
129
+# Running the tests
130
+
131
+You can run all the tests for an exercise by entering
132
+
133
+```sh
134
+$ gradle test
135
+```
136
+
137
+in your terminal.
138
+
139
+## Source
140
+
141
+Conversation with Nate Foster. [http://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf](http://www.cs.cornell.edu/Courses/cs3110/2014sp/hw/0/ps0.pdf)
142
+
143
+## Submitting Incomplete Solutions
144
+
145
+It's possible to submit an incomplete solution so you can see how others have completed the exercise.

+ 18
- 0
exercises/grep/build.gradle 查看文件

@@ -0,0 +1,18 @@
1
+apply plugin: "java"
2
+apply plugin: "eclipse"
3
+apply plugin: "idea"
4
+
5
+repositories {
6
+    mavenCentral()
7
+}
8
+
9
+dependencies {
10
+    testCompile "junit:junit:4.12"
11
+}
12
+
13
+test {
14
+    testLogging {
15
+        exceptionFormat = 'full'
16
+        events = ["passed", "failed", "skipped"]
17
+    }
18
+}

+ 0
- 0
exercises/grep/src/main/java/.keep 查看文件


+ 395
- 0
exercises/grep/src/test/java/GrepToolTest.java 查看文件

@@ -0,0 +1,395 @@
1
+import org.junit.After;
2
+import org.junit.Before;
3
+import org.junit.Ignore;
4
+import org.junit.Test;
5
+
6
+import java.io.IOException;
7
+import java.nio.charset.Charset;
8
+import java.nio.file.Files;
9
+import java.nio.file.Path;
10
+import java.nio.file.Paths;
11
+import java.util.Arrays;
12
+import java.util.Collections;
13
+import java.util.List;
14
+
15
+import static org.junit.Assert.assertEquals;
16
+
17
+public class GrepToolTest {
18
+    private GrepTool grepTool;
19
+
20
+    @Before
21
+    public void setUp() throws IOException {
22
+        List<String> iliadText = Arrays.asList(
23
+            "Achilles sing, O Goddess! Peleus' son;",
24
+            "His wrath pernicious, who ten thousand woes",
25
+            "Caused to Achaia's host, sent many a soul",
26
+            "Illustrious into Ades premature,",
27
+            "And Heroes gave (so stood the will of Jove)",
28
+            "To dogs and to all ravening fowls a prey,",
29
+            "When fierce dispute had separated once",
30
+            "The noble Chief Achilles from the son",
31
+            "Of Atreus, Agamemnon, King of men."
32
+        );
33
+        writeToFile("iliad.txt", iliadText);
34
+
35
+        List<String> midsummerNightText = Arrays.asList(
36
+            "I do entreat your grace to pardon me.",
37
+            "I know not by what power I am made bold,",
38
+            "Nor how it may concern my modesty,",
39
+            "In such a presence here to plead my thoughts;",
40
+            "But I beseech your grace that I may know",
41
+            "The worst that may befall me in this case,",
42
+            "If I refuse to wed Demetrius."
43
+        );
44
+        writeToFile("midsummer-night.txt", midsummerNightText);
45
+
46
+        List<String> paradiseLostText = Arrays.asList(
47
+            "Of Mans First Disobedience, and the Fruit",
48
+            "Of that Forbidden Tree, whose mortal tast",
49
+            "Brought Death into the World, and all our woe,",
50
+            "With loss of Eden, till one greater Man",
51
+            "Restore us, and regain the blissful Seat,",
52
+            "Sing Heav'nly Muse, that on the secret top",
53
+            "Of Oreb, or of Sinai, didst inspire",
54
+            "That Shepherd, who first taught the chosen Seed"
55
+        );
56
+        writeToFile("paradise-lost.txt", paradiseLostText);
57
+
58
+        grepTool = new GrepTool();
59
+    }
60
+
61
+    @After
62
+    public void tearDown() throws IOException {
63
+        deleteFile("iliad.txt");
64
+        deleteFile("midsummer-night.txt");
65
+        deleteFile("paradise-lost.txt");
66
+    }
67
+
68
+    @Test
69
+    public void oneFileOneMatchNoFlags() {
70
+        String expected = "Of Atreus, Agamemnon, King of men.";
71
+
72
+        String actual = grepTool.grep(
73
+            "Agamemnon",
74
+            Collections.emptyList(),
75
+            Collections.singletonList("iliad.txt")
76
+        );
77
+
78
+        assertEquals(expected, actual);
79
+    }
80
+
81
+    @Ignore("Remove to run test")
82
+    @Test
83
+    public void oneFileOneMatchPrintLineNumbersFlag() {
84
+        String expected = "2:Of that Forbidden Tree, whose mortal tast";
85
+
86
+        String actual = grepTool.grep(
87
+            "Forbidden",
88
+            Collections.singletonList("-n"),
89
+            Collections.singletonList("paradise-lost.txt")
90
+        );
91
+
92
+        assertEquals(expected, actual);
93
+    }
94
+
95
+    @Ignore("Remove to run test")
96
+    @Test
97
+    public void oneFileOneMatchCaseInsensitiveFlag() {
98
+        String expected = "Of that Forbidden Tree, whose mortal tast";
99
+
100
+        String actual = grepTool.grep(
101
+            "FORBIDDEN",
102
+            Collections.singletonList("-i"),
103
+            Collections.singletonList("paradise-lost.txt")
104
+        );
105
+
106
+        assertEquals(expected, actual);
107
+    }
108
+
109
+    @Ignore("Remove to run test")
110
+    @Test
111
+    public void oneFileOneMatchPrintFileNamesFlag() {
112
+        String expected = "paradise-lost.txt";
113
+
114
+        String actual = grepTool.grep(
115
+            "Forbidden",
116
+            Collections.singletonList("-l"),
117
+            Collections.singletonList("paradise-lost.txt")
118
+        );
119
+
120
+        assertEquals(expected, actual);
121
+    }
122
+
123
+    @Ignore("Remove to run test")
124
+    @Test
125
+    public void oneFileOneMatchEntireLinesFlag() {
126
+        String expected = "With loss of Eden, till one greater Man";
127
+
128
+        String actual = grepTool.grep(
129
+            "With loss of Eden, till one greater Man",
130
+            Collections.singletonList("-x"),
131
+            Collections.singletonList("paradise-lost.txt")
132
+        );
133
+
134
+        assertEquals(expected, actual);
135
+    }
136
+
137
+    @Ignore("Remove to run test")
138
+    @Test
139
+    public void oneFileOneMatchMultipleFlags() {
140
+        String expected = "9:Of Atreus, Agamemnon, King of men.";
141
+
142
+        String actual = grepTool.grep(
143
+            "OF ATREUS, Agamemnon, KIng of MEN.",
144
+            Arrays.asList("-n", "-i", "-x"),
145
+            Collections.singletonList("iliad.txt")
146
+        );
147
+
148
+        assertEquals(expected, actual);
149
+    }
150
+
151
+    @Ignore("Remove to run test")
152
+    @Test
153
+    public void oneFileSeveralMatchesNoFlags() {
154
+        String expected = "Nor how it may concern my modesty,\n"
155
+            + "But I beseech your grace that I may know\n"
156
+            + "The worst that may befall me in this case,";
157
+
158
+        String actual = grepTool.grep(
159
+            "may",
160
+            Collections.emptyList(),
161
+            Collections.singletonList("midsummer-night.txt")
162
+        );
163
+
164
+        assertEquals(expected, actual);
165
+    }
166
+
167
+    @Ignore("Remove to run test")
168
+    @Test
169
+    public void oneFileSeveralMatchesPrintLineNumbersFlag() {
170
+        String expected = "3:Nor how it may concern my modesty,\n"
171
+            + "5:But I beseech your grace that I may know\n"
172
+            + "6:The worst that may befall me in this case,";
173
+
174
+        String actual = grepTool.grep(
175
+            "may",
176
+            Collections.singletonList("-n"),
177
+            Collections.singletonList("midsummer-night.txt")
178
+        );
179
+
180
+        assertEquals(expected, actual);
181
+    }
182
+
183
+    @Ignore("Remove to run test")
184
+    @Test
185
+    public void oneFileSeveralMatchesMatchEntireLineFlag() {
186
+        String expected = "";
187
+
188
+        String actual = grepTool.grep(
189
+            "may",
190
+            Collections.singletonList("-x"),
191
+            Collections.singletonList("midsummer-night.txt")
192
+        );
193
+
194
+        assertEquals(expected, actual);
195
+    }
196
+
197
+    @Ignore("Remove to run test")
198
+    @Test
199
+    public void oneFileSeveralMatchesCaseInsensitiveFlag() {
200
+        String expected = "Achilles sing, O Goddess! Peleus' son;\n"
201
+            + "The noble Chief Achilles from the son";
202
+
203
+        String actual = grepTool.grep(
204
+            "ACHILLES",
205
+            Collections.singletonList("-i"),
206
+            Collections.singletonList("iliad.txt")
207
+        );
208
+
209
+        assertEquals(expected, actual);
210
+    }
211
+
212
+    @Ignore("Remove to run test")
213
+    @Test
214
+    public void oneFileSeveralMatchesInvertedFlag() {
215
+        String expected = "Brought Death into the World, and all our woe,\n"
216
+            + "With loss of Eden, till one greater Man\n"
217
+            + "Restore us, and regain the blissful Seat,\n"
218
+            + "Sing Heav'nly Muse, that on the secret top\n"
219
+            + "That Shepherd, who first taught the chosen Seed";
220
+
221
+        String actual = grepTool.grep(
222
+            "Of",
223
+            Collections.singletonList("-v"),
224
+            Collections.singletonList("paradise-lost.txt")
225
+        );
226
+
227
+        assertEquals(expected, actual);
228
+    }
229
+
230
+    @Ignore("Remove to run test")
231
+    @Test
232
+    public void oneFileNoMatchesVariousFlags() {
233
+        String expected = "";
234
+
235
+        String actual = grepTool.grep(
236
+            "Gandalf",
237
+            Arrays.asList("-n", "-l", "-x", "-i"),
238
+            Collections.singletonList("iliad.txt")
239
+        );
240
+
241
+        assertEquals(expected, actual);
242
+    }
243
+
244
+    @Ignore("Remove to run test")
245
+    @Test
246
+    public void multipleFilesOneMatchNoFlags() {
247
+        String expected = "iliad.txt:Of Atreus, Agamemnon, King of men.";
248
+
249
+        String actual = grepTool.grep(
250
+            "Agamemnon",
251
+            Collections.emptyList(),
252
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
253
+        );
254
+
255
+        assertEquals(expected, actual);
256
+    }
257
+
258
+    @Ignore("Remove to run test")
259
+    @Test
260
+    public void multipleFilesSeveralMatchesNoFlags() {
261
+        String expected = "midsummer-night.txt:Nor how it may concern my modesty,\n"
262
+            + "midsummer-night.txt:But I beseech your grace that I may know\n"
263
+            + "midsummer-night.txt:The worst that may befall me in this case,";
264
+
265
+        String actual = grepTool.grep(
266
+            "may",
267
+            Collections.emptyList(),
268
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
269
+        );
270
+
271
+        assertEquals(expected, actual);
272
+    }
273
+
274
+    @Ignore("Remove to run test")
275
+    @Test
276
+    public void multipleFilesSeveralMatchesPrintLineNumbersFlag() {
277
+        String expected = "midsummer-night.txt:5:But I beseech your grace that I may know\n"
278
+            + "midsummer-night.txt:6:The worst that may befall me in this case,\n"
279
+            + "paradise-lost.txt:2:Of that Forbidden Tree, whose mortal tast\n"
280
+            + "paradise-lost.txt:6:Sing Heav'nly Muse, that on the secret top";
281
+
282
+        String actual = grepTool.grep(
283
+            "that",
284
+            Collections.singletonList("-n"),
285
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
286
+        );
287
+
288
+        assertEquals(expected, actual);
289
+    }
290
+
291
+    @Ignore("Remove to run test")
292
+    @Test
293
+    public void multipleFilesOneMatchPrintFileNamesFlag() {
294
+        String expected = "iliad.txt\n"
295
+            + "paradise-lost.txt";
296
+
297
+        String actual = grepTool.grep(
298
+            "who",
299
+            Collections.singletonList("-l"),
300
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
301
+        );
302
+
303
+        assertEquals(expected, actual);
304
+    }
305
+
306
+    @Ignore("Remove to run test")
307
+    @Test
308
+    public void multipleFilesSeveralMatchesCaseInsensitiveFlag() {
309
+        String expected = "iliad.txt:Caused to Achaia's host, sent many a soul\n"
310
+            + "iliad.txt:Illustrious into Ades premature,\n"
311
+            + "iliad.txt:And Heroes gave (so stood the will of Jove)\n"
312
+            + "iliad.txt:To dogs and to all ravening fowls a prey,\n"
313
+            + "midsummer-night.txt:I do entreat your grace to pardon me.\n"
314
+            + "midsummer-night.txt:In such a presence here to plead my thoughts;\n"
315
+            + "midsummer-night.txt:If I refuse to wed Demetrius.\n"
316
+            + "paradise-lost.txt:Brought Death into the World, and all our woe,\n"
317
+            + "paradise-lost.txt:Restore us, and regain the blissful Seat,\n"
318
+            + "paradise-lost.txt:Sing Heav'nly Muse, that on the secret top";
319
+
320
+        String actual = grepTool.grep(
321
+            "TO",
322
+            Collections.singletonList("-i"),
323
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
324
+        );
325
+
326
+        assertEquals(expected, actual);
327
+    }
328
+
329
+    @Ignore("Remove to run test")
330
+    @Test
331
+    public void multipleFilesSeveralMatchesInvertedFlag() {
332
+        String expected = "iliad.txt:Achilles sing, O Goddess! Peleus' son;\n"
333
+            + "iliad.txt:The noble Chief Achilles from the son\n"
334
+            + "midsummer-night.txt:If I refuse to wed Demetrius.";
335
+
336
+        String actual = grepTool.grep(
337
+            "a",
338
+            Collections.singletonList("-v"),
339
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
340
+        );
341
+
342
+        assertEquals(expected, actual);
343
+    }
344
+
345
+    @Ignore("Remove to run test")
346
+    @Test
347
+    public void multipleFilesOneMatchEntireLinesFlag() {
348
+        String expected = "midsummer-night.txt:But I beseech your grace that I may know";
349
+
350
+        String actual = grepTool.grep(
351
+            "But I beseech your grace that I may know",
352
+            Collections.singletonList("-x"),
353
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
354
+        );
355
+
356
+        assertEquals(expected, actual);
357
+    }
358
+
359
+    @Ignore("Remove to run test")
360
+    @Test
361
+    public void multipleFilesOneMatchMultipleFlags() {
362
+        String expected = "paradise-lost.txt:4:With loss of Eden, till one greater Man";
363
+
364
+        String actual = grepTool.grep(
365
+            "WITH LOSS OF EDEN, TILL ONE GREATER MAN",
366
+            Arrays.asList("-n", "-i", "-x"),
367
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
368
+        );
369
+
370
+        assertEquals(expected, actual);
371
+    }
372
+
373
+    @Ignore("Remove to run test")
374
+    @Test
375
+    public void multipleFilesNoMatchesVariousFlags() {
376
+        String expected = "";
377
+
378
+        String actual = grepTool.grep(
379
+            "Frodo",
380
+            Arrays.asList("-n", "-l", "-i", "-x"),
381
+            Arrays.asList("iliad.txt", "midsummer-night.txt", "paradise-lost.txt")
382
+        );
383
+
384
+        assertEquals(expected, actual);
385
+    }
386
+
387
+    private void writeToFile(String filename, List<String> contents) throws IOException {
388
+        Path file = Paths.get(filename);
389
+        Files.write(file, contents, Charset.forName("UTF-8"));
390
+    }
391
+
392
+    private void deleteFile(String filename) throws IOException {
393
+        Files.deleteIfExists(Paths.get(filename));
394
+    }
395
+}

+ 1
- 0
exercises/settings.gradle 查看文件

@@ -34,6 +34,7 @@ include 'forth'
34 34
 include 'gigasecond'
35 35
 include 'go-counting'
36 36
 include 'grade-school'
37
+include 'grep'
37 38
 include 'hamming'
38 39
 include 'hexadecimal'
39 40
 include 'hello-world'