Синхронизация чтения дротиком большого файла

#dart #async-await

#дротик #асинхронное ожидание

Вопрос:

Мне нужна возможность синхронного чтения большого файла с разделителями табуляции по строке за раз. Я нашел это асинхронное решение. Можно ли выполнить синхронизацию с помощью await? Или есть лучший метод синхронизации?

 void readFileStream() {
  Stream<List<int>> stream = new File('./assets/user.json').openRead();
  StringBuffer buffer = new StringBuffer();
  stream
    .transform(utf8.decoder)
    .listen((data) {
      buffer.write(data);
    },
    onDone: () => print(buffer.toString()),
    onError: (e) => print(e));
}
  

Комментарии:

1. Вы не можете сделать асинхронные функции синхронными. Однако File уже предоставляет как асинхронные, так и синхронные интерфейсы. Например, File.readAsStringSync .

2. @jamesdlin Я думаю, что вопрос в том, что OP хочет иметь возможность перебирать каждую отдельную строку в файле, который слишком велик, чтобы открывать все сразу в памяти, но делать это синхронно. Теоретически это возможно с помощью File.openSync и последующего использования RandomAccessFile.readByteSync для декодирования символов по одному до тех пор, пока не будет обнаружен перевод строки, но в этот момент я должен задаться вопросом, стоит ли вообще не просто выполнять все это асинхронно. (Я не знаю встроенного метода, который делает это за вас.)

Ответ №1:

Вы не можете сделать асинхронную операцию синхронной с помощью await или любым другим способом. Быть синхронным означает немедленное возвращение результата, быть асинхронным означает, что результат не доступен немедленно.

Для синхронного чтения вы можете читать по байту за раз, пока не дойдете до новой строки, но это, вероятно, будет очень медленно. Более разумный подход заключается в чтении фрагментов и разделении их на строки.

Может быть, что-то вроде:

 import "dart:io";
import "dart:convert";
import "dart:collection";

Iterable<String> readLinesSync(File file) sync* {
  var f = file.openSync(); // creates a RandomAccessFile
  var lineBuffer = Queue<String>();
  var sink = utf8.decoder.startChunkedConversion(
    LineSplitter().startChunkedConversion(_Sink(lineBuffer.add)));

  bool done = false;
  do {
    do {
      var chunk = f.readSync(256);
      sink.add(chunk);
      if (chunk.length < 256) {
        done = true;
        sink.close(); 
        break; 
      }
    } while (lineBuffer.isEmpty)
    while (lineBuffer.isNotEmpty) {
      yield lineBuffer.removeFirst(); 
    }
  } while (!done);
}

class _Sink<T> implements Sink<T> {
  void Function(T) _add;
  _Sink(this._add);
  void add(T value) {
    _add(value);
  }
  void close() {}
}
  

Комментарии:

1. Я ценю обратную связь. Я понимаю, что асинхронность и синхронизация — это не одно и то же, но я надеялся объединить необходимые асинхронные функции в одну асинхронную функцию, а затем немного синхронизировать, вызывая их по порядку и используя await для каждой из них. Итак, я надеялся, что метод, который я перечислил, может быть разбит на несколько вызовов, которым может предшествовать «await»

2. Возможно, вы сможете использовать SteamIterator библиотеки платформы или StreamQueue из package:async для чтения созданного вами потока постепенно. Будет ли это что-то вроде того, что вы ищете?