Skip to content

Commit db3b8c4

Browse files
committed
wc command with flags and concurrency support
- Implemented the wc (word count) command in Java, supporting flags for counting lines (-l), words (-w), bytes (-c), and characters (-m). - Handles standard input, small files, and large files (over 10MB) with concurrency for efficiency. - Error handling and edge case management to ensure robust functionality across various scenarios.
1 parent 7dfe522 commit db3b8c4

File tree

2 files changed

+198
-2
lines changed

2 files changed

+198
-2
lines changed
Lines changed: 196 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,199 @@
11
package com.unixtools.command.filecontent;
22

3-
public class WcCommand {
4-
3+
import com.unixtools.core.Command;
4+
5+
import java.io.*;
6+
import java.nio.charset.StandardCharsets;
7+
import java.nio.file.*;
8+
import java.util.*;
9+
import java.util.concurrent.*;
10+
11+
public class WcCommand implements Command {
12+
private static final long LARGE_FILE_THRESHOLD = 10 * 1024 * 1024;
13+
private static final String VALID_FLAGS = "lwcm";
14+
private boolean countLines = false;
15+
private boolean countWords = false;
16+
private boolean countBytes = false;
17+
private boolean countChars = false;
18+
19+
private static class Counters {
20+
int lines = 0;
21+
int words = 0;
22+
int bytes = 0;
23+
int characters = 0;
24+
}
25+
26+
@Override
27+
public void execute(String[] args) {
28+
List<String> flags = new ArrayList<>();
29+
List<String> filePaths = new ArrayList<>();
30+
parseArguments(args, flags, filePaths);
31+
32+
if (!areFlagsValid(flags)) {
33+
System.out.println("Invalid flags. Valid flags are: " + VALID_FLAGS);
34+
return;
35+
}
36+
37+
if (flags.isEmpty()) {
38+
countLines = countWords = countBytes = countChars = true;
39+
}
40+
41+
String filePath = filePaths.isEmpty() ? null : filePaths.get(0);
42+
processFile(filePath);
43+
}
44+
45+
private void parseArguments(String[] args, List<String> flags, List<String> paths) {
46+
for (String arg : args) {
47+
if (arg.startsWith("-")) {
48+
for (char flag : arg.substring(1).toCharArray()) {
49+
flags.add(String.valueOf(flag));
50+
switch (flag) {
51+
case 'l':
52+
countLines = true;
53+
break;
54+
case 'w':
55+
countWords = true;
56+
break;
57+
case 'c':
58+
countBytes = true;
59+
break;
60+
case 'm':
61+
countChars = true;
62+
break;
63+
}
64+
}
65+
} else {
66+
paths.add(arg);
67+
}
68+
}
69+
}
70+
71+
private boolean areFlagsValid(List<String> flags) {
72+
return flags.stream().allMatch(flag -> VALID_FLAGS.contains(flag));
73+
}
74+
75+
private void processFile(String filePath) {
76+
Counters counters = new Counters();
77+
78+
if (filePath == null) {
79+
processStandardInput(counters);
80+
return;
81+
}
82+
83+
Path path = Paths.get(filePath);
84+
if (!Files.exists(path) || !Files.isReadable(path)) {
85+
System.err.println("File does not exist or is not readable: " + filePath);
86+
return;
87+
}
88+
89+
try {
90+
long fileSize = Files.size(path);
91+
if (fileSize >= LARGE_FILE_THRESHOLD) {
92+
processLargeFileConcurrently(path, fileSize, counters);
93+
} else {
94+
processSmallFile(path, counters);
95+
}
96+
} catch (IOException e) {
97+
System.err.println("Error processing file:" + e.getMessage());
98+
}
99+
}
100+
101+
private void countFlagCounters(String line, Counters counters) {
102+
if (countLines) {
103+
counters.lines++;
104+
}
105+
if (countWords) {
106+
counters.words += new StringTokenizer(line).countTokens();
107+
}
108+
if (countChars) {
109+
counters.characters += line.length() + System.lineSeparator().length();
110+
}
111+
if (countBytes) {
112+
counters.bytes += line.getBytes(StandardCharsets.UTF_8).length
113+
+ System.lineSeparator().getBytes(StandardCharsets.UTF_8).length;
114+
}
115+
}
116+
117+
private void processStandardInput(Counters counters) {
118+
try (Scanner scanner = new Scanner(System.in)) {
119+
while (scanner.hasNextLine()) {
120+
String line = scanner.nextLine();
121+
countFlagCounters(line, counters);
122+
printCounts(counters);
123+
}
124+
}
125+
}
126+
127+
private void processSmallFile(Path path, Counters counters) throws IOException {
128+
try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
129+
String line;
130+
while ((line = reader.readLine()) != null) {
131+
countFlagCounters(line, counters);
132+
}
133+
}
134+
printCounts(counters);
135+
}
136+
137+
private void processLargeFileConcurrently(Path path, long fileSize, Counters counters) throws IOException {
138+
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
139+
long segmentSize = fileSize / Runtime.getRuntime().availableProcessors();
140+
List<Future<int[]>> futures = new ArrayList<>();
141+
142+
for (long offset = 0; offset < fileSize; offset += segmentSize) {
143+
final long startOffset = offset;
144+
final long endOffset = Math.min(offset + segmentSize, fileSize);
145+
futures.add(executor.submit(() -> processFileSegment(path, startOffset, endOffset, counters)));
146+
}
147+
148+
for (Future<int[]> future : futures) {
149+
try {
150+
int[] counts = future.get();
151+
if (countLines) {
152+
counters.lines += counts[0];
153+
}
154+
if (countWords) {
155+
counters.words += counts[1];
156+
}
157+
if (countBytes) {
158+
counters.bytes += counts[2];
159+
}
160+
if (countChars) {
161+
counters.characters += counts[3];
162+
}
163+
} catch (InterruptedException | ExecutionException e) {
164+
System.err.println("Error processing file segment: " + e.getMessage());
165+
}
166+
}
167+
168+
executor.shutdown();
169+
printCounts(counters);
170+
}
171+
172+
private int[] processFileSegment(Path path, long startOffset, long endOffset, Counters counters) throws IOException {
173+
try (RandomAccessFile file = new RandomAccessFile(path.toFile(), "r")) {
174+
file.seek(startOffset);
175+
String line;
176+
while (file.getFilePointer() < endOffset && (line = file.readLine()) != null) {
177+
countFlagCounters(line, counters);
178+
}
179+
}
180+
181+
return new int[] { counters.lines, counters.words, counters.bytes, counters.characters };
182+
}
183+
184+
private void printCounts(Counters counters) {
185+
if (countLines) {
186+
System.out.print("Lines: " + counters.lines + " ");
187+
}
188+
if (countWords) {
189+
System.out.print("Words: " + counters.words + " ");
190+
}
191+
if (countBytes) {
192+
System.out.print("Bytes: " + counters.bytes + " ");
193+
}
194+
if (countChars) {
195+
System.out.print("Characters: " + counters.characters);
196+
}
197+
System.out.println();
198+
}
5199
}

src/com/unixtools/core/CommandFactory.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public static Command getCommand(String commandName) {
6262
return new SortCommand();
6363
case "uniq":
6464
return new UniqCommand();
65+
case "wc":
66+
return new WcCommand();
6567
default:
6668
return null;
6769
}

0 commit comments

Comments
 (0)