Skip to content

Commit 175c44f

Browse files
committed
feat(wc): implement basic word count utility
- Add wc/wc.js: reads files and outputs line, word, and byte counts - Support multiple files with totals row - Use fs.promises.readFile for async file reading - Add error handling for missing files with non-zero exit code - Format output to match standard wc utility (padded columns) - docs(wc): normalize README bullets so example commands render correctly
1 parent e6fccc8 commit 175c44f

2 files changed

Lines changed: 72 additions & 5 deletions

File tree

implement-shell-tools/wc/README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ Your task is to implement your own version of `wc`.
66

77
It must act the same as `wc` would, if run from the directory containing this README.md file, for the following command lines:
88

9-
* `wc sample-files/*`
10-
* `wc -l sample-files/3.txt`
11-
* `wc -w sample-files/3.txt`
12-
* `wc -c sample-files/3.txt`
13-
* `wc -l sample-files/*`
9+
- `wc sample-files/*`
10+
- `wc -l sample-files/3.txt`
11+
- `wc -w sample-files/3.txt`
12+
- `wc -c sample-files/3.txt`
13+
- `wc -l sample-files/*`
1414

1515
Matching any additional behaviours or flags are optional stretch goals.
1616

implement-shell-tools/wc/wc.js

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import process from "node:process";
2+
import { promises as fs } from "node:fs";
3+
4+
/**
5+
* Calculates the lines, words, and bytes for a given file.
6+
*/
7+
async function calculateFileStats(filePath) {
8+
const fileBuffer = await fs.readFile(filePath);
9+
const fileContent = fileBuffer.toString();
10+
11+
// Standard 'wc' counts newline characters (\n)
12+
const lineCount = fileContent.split("\n").length - 1;
13+
14+
// Split by any whitespace and filter out empty strings to get actual words
15+
const wordCount = fileContent
16+
.split(/\s+/)
17+
.filter((word) => word.length > 0).length;
18+
19+
// Buffer.length provides the exact byte count on disk
20+
const byteCount = fileBuffer.length;
21+
22+
return { lineCount, wordCount, byteCount, filePath };
23+
}
24+
25+
/**
26+
* Main execution function
27+
*/
28+
async function runWordCount() {
29+
const filePaths = process.argv.slice(2);
30+
const results = [];
31+
32+
for (const filePath of filePaths) {
33+
try {
34+
const stats = await calculateFileStats(filePath);
35+
results.push(stats);
36+
37+
printFormattedLine(
38+
stats.lineCount,
39+
stats.wordCount,
40+
stats.byteCount,
41+
stats.filePath,
42+
);
43+
} catch (error) {
44+
console.error(`wc: ${filePath}: No such file or directory`);
45+
process.exitCode = 1;
46+
}
47+
}
48+
49+
// If multiple files were processed, show the total
50+
if (results.length > 1) {
51+
const totalLines = results.reduce((sum, item) => sum + item.lineCount, 0);
52+
const totalWords = results.reduce((sum, item) => sum + item.wordCount, 0);
53+
const totalBytes = results.reduce((sum, item) => sum + item.byteCount, 0);
54+
55+
printFormattedLine(totalLines, totalWords, totalBytes, "total");
56+
}
57+
}
58+
59+
/**
60+
* Formats the output into columns to match the standard 'wc' utility
61+
*/
62+
function printFormattedLine(lines, words, bytes, label) {
63+
const format = (number) => String(number).padStart(8);
64+
console.log(`${format(lines)}${format(words)}${format(bytes)} ${label}`);
65+
}
66+
67+
runWordCount();

0 commit comments

Comments
 (0)