Как мне прочитать локальный файл в Deno?

Я пишу программу подсчета слов, написанную на TypeScript, которую я пытаюсь запустить в Deno. Я вызываю это без аргументов, просто deno ./word_count.ts поэтому он должен иметь доступ по умолчанию к файловой системе только для чтения. Я надеялся, что смогу использовать стандартный браузер fetch() API с file: Схема URL для чтения из файловой системы, например:

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.

Но когда я пытаюсь, я вижу, что вижу fetch не поддерживает file URL-адрес:

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)

Как я могу прочитать содержимое локального файла в Deno?

1 ответ

Решение

Дено включает в себя readFileSync(filename: string): Uint8Array функция в стандартной библиотеке, которая доступна по специальному пути импорта 'deno',

import {readFileSync} from 'deno';

const bytes = readFileSync('./input.txt'); 

Это читает содержимое файла как байтовый массив. Если файл содержит текст, который вы хотите в виде строки, как в вашем примере, вы можете использовать стандартный ECMAScript TextDecoder класс ( MDN документы).

const utf8Decoder = new TextDecoder('utf-8');
const string = utf8Decoder.decode(bytes);

Для исходного примера это дает нам:

word_count.ts
import {readFileSync} from 'deno';

const countWords = (s: string): number =>
s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;

const main = async () => {
    const decoder = new TextDecoder("utf-8");
    const bytes = readFileSync("./input.txt");
    const text = decoder.decode(bytes);
    const count = countWords(text);
    console.log(`I read ${count} words.`);
};

main();

Который производит желаемый результат:

 $ deno./word_count.ts 
 Я прочитал 9 слов. 

С помощью readFileStr от deno_std

Пример использования readFileStr от std/fs:

import { readFileStr } from 'https://deno.land/std/fs/mod.ts';

const countWords = (s: string): number =>
  s.split(/\s+/g).filter(w => /[a-z0-9]/.test(w)).length;

const main = async () => {
  const text = await readFileStr('input.txt');
  const count = countWords(text);
  console.log(`I read ${count} words.`);
};

main();

Вы можете скачать меньше, просто используя readFileStr от std/fs изменив первую строку на:

import { readFileStr } from 'https://deno.land/std/fs/read_file_str.ts';

Смотри документы по адресу: https://github.com/denoland/deno_std

Использование встроенного 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 main = async () => {
  const text = decoder.decode(await Deno.readFile('input.txt'));
  const count = countWords(text);
  console.log(`I read ${count} words.`);
};

main();

Обратите внимание, что вам нужно явно декодировать данные как UTF-8.

См. Документы по адресу: https://deno.land/t ypedoc/index.html.

Использование блокировки чтения

Принятый ответ использует readFileSync() которая является функцией блокировки, поэтому main() являющийся async не нужен Упрощенный (и рабочий) пример будет:

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.`);

Обратите внимание, что нет asyncнет main() и нет await где угодно, код проще, но Deno.readFileSync() будет блокировать поток до тех пор, пока файл не будет прочитан - для простого скрипта, который выполняет последовательность шагов, как в этом примере, это нормально, но если бы он был внутри обработчика запросов на сервере, это было бы катастрофой для производительности.

Использование нижнего уровня Deno.open а также 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 main = async () => {
  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.`);
};

main();

Вы могли бы поставить первые две строки main() в одну строку:

const text = decoder.decode(await Deno.readAll(await Deno.open('input.txt')));

но это было бы менее читабельным.

См. Документы по адресу: https://deno.land/t ypedoc/index.html.

Еще более низкий уровень Deno.open а также Deno.read

Вы могли бы использовать даже нижний рычаг Deno.read но тогда вы также должны были бы выделить буферы

См. Документы по адресу: https://deno.land/t ypedoc/index.html

С помощью new File() абстракция

Существует также абстракция в стиле класса для чтения и записи файлов.

См. Документы по адресу: https://deno.land/t ypedoc/classes/deno.file.html

стандартное 0.62.0 / Deno 1.2.1 +

поскольку std v0.62.0, readFileStr а также readFileStrSyncбудут удалены из стандартной библиотеки.

Deno.readTextFile а также Deno.readTextFileSync имеют тот же API, и теперь это лучший способ.

(то же самое для writeFileStr / writeFileStrSync)

Другие вопросы по тегам