public class FileSearcher
1: import java.io.File;
2: import java.io.FileNotFoundException;
3: import java.util.ArrayList;
4: import java.util.HashMap;
5: import java.util.List;
6: import java.util.Map;
7: import java.util.Scanner;
8: import java.util.Set;
9: import java.util.TreeSet;
11: /**
12: * A program that uses a Map from Strings to Sets of Integers to do efficient
13: * searches of a text file. Sadly, the search is for a single word at a time.
14: * Try it on THIS file!
15: *
16: * @author Mark Young (A00000000)
17: */
18: public class FileSearcher {
20: private static final Scanner KBD = new Scanner(System.in);
22: public static void main(String[] args) {
23: printIntroduction();
24: List<String> lines = readLines();
25: Map<String, Set<Integer>> locations = makeLocationMap(lines);
26: search(lines, locations);
27: }
29: /**
30: * Tell user what you do.
31: */
32: private static void printIntroduction() {
33: System.out.println("\n"
34: + "This program lets you search a file.\n");
35: }
37: /**
38: * Get the file and read its lines.
39: */
40: private static List<String> readLines() {
41: // create required variables
42: String fileName;
43: List<String> contents = new ArrayList<>();
45: // get file name
46: System.out.print("Enter a file name: ");
47: fileName = KBD.nextLine();
48:
49: // try to read the file
50: try (Scanner file = new Scanner(new File(fileName))) {
51:
52: // file exists: read it
53: while (file.hasNextLine()) {
54: contents.add(file.nextLine());
55: }
56: } catch (FileNotFoundException fnf) {
57:
58: // file not found: exit program
59: System.err.println("\nSorry, I can't open that file.");
60: System.exit(1);
61: }
63: return contents;
64: }
66: /**
67: * Make a map showing where each word is. The map generated uses each word
68: * present in the list as a key to a set of line numbers. The line numbers
69: * correspond to the position in the given list. If a line number is in the
70: * set, then the word is on that line.
71: *
72: * The map is case insensitive, with the lower-case version of the word
73: * being used as the key.
74: *
75: * Lines are split on white-space and punctuation.
76: *
77: * NOTE: because the lines are split on punctuation, contractions are split
78: * into two parts -- so "doesn't" gets recorded as two words, "doesn" and
79: * "t". An exercise for the reader is to come up with a plan to replace
80: * contractions in a way that'd allow them to be handled better.
81: *
82: * @param lines the lines to make the map from.
83: * @return a map from words to the numbers of the lines it appears on.
84: */
85: private static Map<String, Set<Integer>> makeLocationMap(
86: List<String> lines
87: ) {
88: // create required variables
89: Map<String, Set<Integer>> result = new HashMap<>();
90:
91: // for each line in the file (saved in list of lines)
92: for (int lineNumber = 0; lineNumber < lines.size(); ++lineNumber) {
93:
94: // get the line
95: String line = lines.get(lineNumber);
96:
97: // break the line up on spaces and punctuation
98: for (String word : line.split("[\\s\\p{Punct}]+")) {
99:
100: // save word in loswer case (for case insensitive search)
101: word = word.toLowerCase();
102: if (result.containsKey(word)) {
103: // we've already seen this word before: add to its set
104: result.get(word).add(lineNumber);
105: } else {
106: // haven't seen this word before: make a new set
107: Set<Integer> s = new TreeSet<>();
108: s.add(lineNumber);
109: result.put(word, s);
110: }
111: }
112: }
113: return result;
114: }
116: /**
117: * Repeatedly look for a word in the file.
118: *
119: * @param lines the list of lines from the file.
120: * @param locations the map from words to line numbers.
121: */
122: private static void search(
123: List<String> lines,
124: Map<String, Set<Integer>> locations
125: ) {
126: // create required variables
127: String searchWord;
128: Set<Integer> lineNumbers;
129:
130: // get the search word (ONE WORD ONLY!)
131: System.out.print("\nEnter the word you'd like to find: ");
132: searchWord = KBD.nextLine();
133:
134: // quit when there is no word
135: while (!searchWord.equals("")) {
136:
137: // switch to lower case (for case insensitive search)
138: searchWord = searchWord.toLowerCase();
139:
140: // check to see if it's there
141: if (locations.containsKey(searchWord)) {
142:
143: // it's there: print out the lines (with numbers)
144: System.out.println("Your word appears in these lines: ");
145: lineNumbers = locations.get(searchWord);
146: for (int lineNo : lineNumbers) {
147: System.out.printf("%6d: %s%n",
148: lineNo, lines.get(lineNo));
149: }
150: } else {
151:
152: // not there: report failure
153: System.out.println("Your word doesn't appear in any line.");
154: }
156: // get next search word
157: System.out.print("\nEnter the word you'd like to find: ");
158: searchWord = KBD.nextLine();
159: }
160: }
162: }