I'm writing a word count program written in TypeScript that I'm trying to run in Deno. I'm invoking it with no arguments, just deno ./word_count.ts
, so it should have the default read-only filesystem access. I was hoping that I might be able to use the standard browser fetch()
API with the file:
URL scheme to read from the filesystem, like this:
word_count.ts
const countWords = (s: string): number =>
s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;
const main = async () => {
const text = await (await fetch("file:///./input.txt")).text();
const count = countWords(text);
console.log(`I read ${count} words.`);
};
main();
input.txt
The quick brown fox jumps over the lazy dog.
But when I try I see I see that fetch
doesn't support file
URLs:
Error: an error occurred trying to connect: invalid URL, scheme must be http
at FetchResponse.onMsg (deno/js/fetch.ts:121:21)
at FetchRequest.onMsg (deno/js/fetch.ts:152:19)
at onFetchRes (deno/js/fetch.ts:28:8)
at onMessage$1 (deno/js/main.ts:30:7)
How can I read the contents of a local file in Deno?
(Update: wrapping code in async function main() { ... }
is no longer needed because Deno now supports top-level await
)
Deno.readTextFile
const countWords = (s: string): number =>
s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;
const text = await Deno.readTextFile('input.txt');
const count = countWords(text);
console.log(`I read ${count} words.`);
See the docs at: https://doc.deno.land/builtin/stable#Deno.readTextFile
Deno.readFile
const countWords = (s: string): number =>
s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(await Deno.readFile('input.txt'));
const count = countWords(text);
console.log(`I read ${count} words.`);
Note that you need to explicitly decode the data as UTF-8.
See the docs at: https://deno.land/typedoc/index.html#readfile
The accepted answer uses readFileSync()
which is a blocking function so the main()
being async
is not needed (Update: it is no longer needed for non-blocking await
as well). A simplified (and working) example would be:
const countWords = (s: string): number =>
s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;
const decoder = new TextDecoder('utf-8');
const text = decoder.decode(Deno.readFileSync('input.txt'));
const count = countWords(text);
console.log(`I read ${count} words.`);
Note that there is no await
anywhere, so the code is slightly simpler (Update: before Deno supported top-level await
the difference in simplicity was bigger) but the Deno.readFileSync()
will block the thread until the file is read - for a simple script that does a sequence of steps like in this example this is fine, but if it was inside a request handler in a server then it would be a disaster for the performance.
Deno.open
and Deno.readAll
const countWords = (s: string): number =>
s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;
const decoder = new TextDecoder('utf-8');
const file = await Deno.open('input.txt');
const text = decoder.decode(await Deno.readAll(file));
const count = countWords(text);
console.log(`I read ${count} words.`);
You could put the first two lines of main()
in a single line:
const text = decoder.decode(await Deno.readAll(await Deno.open('input.txt')));
but it would be less readable.
See the docs at: https://deno.land/typedoc/index.html#readall
Deno.open
and Deno.read
You could use even lower-lever Deno.read
but then you'd also have to allocate the buffers
See the docs at: https://deno.land/typedoc/index.html#read
new File()
abstractionThere is also a class-style abstraction for reading and writing files.
See the docs at: https://deno.land/typedoc/classes/deno.file.html