How to copy a Stream<String> in dart? - dart

How do we copy a Stream<String> in dart?
How can we loop over
Stream<String> blocks(File file) {
return file.openRead()
.transform(utf8.decoder)
.transform(LineSplitter());
}
more than once?

Why not just call blocks each time you need to loop over the data?
import 'dart:io';
import 'dart:convert';
Future<void> main() async {
final file = File('<INSERT SOME FILE NAME HERE>');
await for (String line in blocks(file)) {
print('stream 1: $line');
}
await for (String line in blocks(file)) {
print('stream 2: $line');
}
}
Stream<String> blocks(File file) {
return file.openRead().transform(utf8.decoder).transform(LineSplitter());
}

Related

SQFLITE ERROR: SqfliteDatabaseException (DatabaseException(database_closed))

The two pages in the application are listed with database(sqlite). but when I want to switch between pages, I get such an error: SqfliteDatabaseException (DatabaseException(database_closed))
please help mee.. I don't understand why see this error.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:bankingapp/models/coin.dart';
import 'package:bankingapp/models/histories.dart';
import 'package:flutter/services.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class DbHelper {
static Database? _db;
Future<Database> get db async {
return _db ??= await initDb();
}
Future<Database> initDb() async {
var dbFolder = await getDatabasesPath();
String path = join(dbFolder, 'app.db');
// Delete any existing database:
await deleteDatabase(path);
// Create the writable database file from the bundled demo database file:
try {
await Directory(dirname(path)).create(recursive: true);
} catch (_) {}
ByteData data =
await rootBundle.load(join("assets/database", "bankingapp.db"));
List<int> bytes =
data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await new File(path).writeAsBytes(bytes, flush: true);
//open the database
return await openDatabase(path);
}
Future<List<Histories>> getHistories() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Histories");
return result.map((data) => Histories.fromMap(data)).toList();
}
Future<List<Coins>> getCoins() async {
var dbClient = await db;
var result = await dbClient.rawQuery("SELECT * FROM Coins");
return result.map((data) => Coins.fromMap(data)).toList();
}
}
I don't exactly know the answer but I suggest you to use Floor package. This is really simple to implement and it is the abstraction of SQLite database so I think it will be familiar to you. I don't seem to face such error with this package.
https://pub.dev/packages/floor

Get the list of files from directory

I am trying to add the content of directory using this approach and asign it to List but not sure why it's not working. I see it's async problem but not sure how to solve it.I expect to return the list of files from retCont() but instead it's returning empty list.
import 'dart:io';
void main() async {
print('CONT: ${await retCont()}');
}
Future retCont() async {
var myDir = Directory.current;
List cont = [];
await myDir.list().listen((FileSystemEntity entity) {
print(entity.path);
cont.add(entity.path);
});
return cont;
}
listen() returns a StreamSubscription<FileSystemEntity> which is not a Future so you cannot await on that.
The list() call returns Stream<FileSystemEntity>. For streams you can instead of await and listen() use await for like this:
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() async {
final myDir = Directory.current;
final cont = <String>[];
await for (final entity in myDir.list()) {
print(entity.path);
cont.add(entity.path);
}
return cont;
}
And as a bonus fact, the same program can be written like this if you skip the print(entity.path);.
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() =>
Directory.current.list().map((event) => event.path).toList();
If you really want to use StreamSubscription I think the easiest way is to use a Completer instance which you can complete when you have got all the elements from List():
import 'dart:async';
import 'dart:io';
Future<void> main() async {
print('CONT: ${await retCont()}');
}
Future<List<String>> retCont() {
final myDir = Directory.current;
final cont = <String>[];
final completer = Completer<List<String>>();
myDir.list().listen((FileSystemEntity entity) {
print(entity.path);
cont.add(entity.path);
}, onDone: () => completer.complete(cont));
return completer.future;
}

Dart read json objects from Uneditable large file

i have a large file which that uneditable and i have more jsonObject into that, for example:
{"id":"#123456","v":"1"}
this isn't jsonArray which i can't parse and read it, for example:
String file= new File('file.json').readAsStringSync();
Map<String, dynamic> data = json.decode(file);
you think how can i read this objects from file and show key and values?
Try the following solution. I ended up making a parser for your input format which are returning each parsed JSON object in a Stream.
The parser will not work if any strings in your JSON contains { or }. If that is the case, I can expand the parser so it takes this into account but I don't want to make it more advanced than necessary.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final ids = await File('large_file.json')
.openRead()
.transform(const Utf8Decoder())
.transform<dynamic>(JsonObjectTransformer())
.map((dynamic json) => json['id'] as String)
.toList();
print(ids); // [#123456, #123456]
}
class JsonObjectTransformer extends StreamTransformerBase<String, dynamic> {
static final _openingBracketChar = '{'.codeUnitAt(0);
static final _closingBracketChar = '}'.codeUnitAt(0);
#override
Stream<dynamic> bind(Stream<String> stream) async* {
final sb = StringBuffer();
var bracketsCount = 0;
await for (final string in stream) {
for (var i = 0; i < string.length; i++) {
final current = string.codeUnitAt(i);
sb.writeCharCode(current);
if (current == _openingBracketChar) {
bracketsCount++;
}
if (current == _closingBracketChar && --bracketsCount == 0) {
yield json.decode(sb.toString());
sb.clear();
}
}
}
}
}

How to store csv data in a list

I want to read a csv file and store the content in a list of abjects. So I created the respective object and made an inputstream to read the file. But when I try to acces the list from outside of the function it's empty, so I thing, that the data is stored in a copy of listOfStocks and not directly in the list, like I want. So how can I store the content in the list?
import 'dart:io';
import 'dart:async';
import 'dart:convert';
main(List arguments) {
final File file = new File("../Data/In.List.toDart.csv");
Stream<List> inputStream = file.openRead();
List<Stock> listOfStocks = new List();
inputStream
.transform(utf8.decoder)
.transform(new LineSplitter())
.listen((String line) {
List row = line.split(',');
Stock stock = new Stock();
stock.setSymbol(row[0]);
stock.setDesc(row[1]);
stock.setExchange(row[2]);
listOfStocks.add(stock);
print(listOfStocks.length);
},
onDone: () { print('All read.'); },
onError: (e) { print(e.toString()); });
print(listOfStocks.length);
}
class Stock {
String symbol;
String desc;
String exchange;
void setSymbol(String symbol) {
this.symbol = symbol;
}
void setDesc(String desc) {
this.desc = desc;
}
void setExchange(String exchange) {
this.exchange = exchange;
}
String getSymbol() {
return symbol;
}
String getDesc() {
return desc;
}
String getExchange() {
return exchange;
}
}
The inputStream is asynchronous, so it will take some time before the file has been read.
The inputStream.transform(...).transform(...).listen(...) call completes immediately, then the stream will emit events when something has been read from the disk, and the listOfStocks will be gradually populated.
If you need the content of the list "immediately", that is: before you do anything else, you will have to block until the list is fully populated.
You can do this in two ways:
Read the file synchronously. This makes the operating system block until the file has been read completely into memory.
var content = file.readAsStringSync(); // Defaults to UTF-8 decoding.
for (var line in LineSplitter.split(content))
...
lineOfStocks.add(stock);
}
print(listOfStocks.length);
Read the file asynchronously, but wait for the transformation to complete:
main(List arguments) async {
...
await inputStream
.transform(utf8.decoder)
.transform(const LineSplitter())
.forEach((String line) { // Use forEach instead of listen
...
listOfStocks.add(stock);
});
print(listOfStocks.length);
}
I you use forEach instead of listen, it returns a future which completes
when the stream is done. With listen, you get a done event that you have
to handle manually.

Prevent to read file many times

I am trying to write an i18n app. The program read a json file, that contains translation from languages and it based on json structure.
{
"EN": {
"TEXT1": "Hello",
"TEXT2": "March"
},
"DE": {
"TEXT1": "Hallo",
"TEXT2": "März"
}
}
My program read the json file in async way with the file class, the whole code
import 'dart:io';
import 'dart:async';
import 'package:json_object/json_object.dart';
abstract class I18n {
static _I18n _i18n;
factory I18n(String file, String lang) {
if(_i18n == null) {
_i18n = new _I18n(file, lang);
return _i18n;
}
return _i18n;
}
Future<String> getTextByMap(String textId);
}
class _I18n implements I18n {
File _file;
String _lang;
JsonObject _jsonContainer;
JsonObject _jsonFiltered;
Future<JsonObject> _imme;
// Parameters:
// file: The whole path and filename
// lang: Expected language
_I18n(String file, this._lang) {
this._file = new File(file);
}
// Read file and return the content of file.
Future<String> _readFileFromStream() {
var com = new Completer();
this._file.exists()
.then((fileExists) {
if(!fileExists) {
throw new StateError('File not found');
}
return this._file.readAsString()
.then((stream) => com.complete(stream));
});
return com.future;
}
void _convertContentToJson(String stream) {
this._jsonContainer = new JsonObject.fromJsonString(stream);
}
Future<JsonObject> _prepareData() {
return this._readFileFromStream().then((stream) {
_convertContentToJson(stream);
this._jsonFiltered = this._jsonContainer[this._lang];
return this._jsonFiltered;
});
}
Future<String> getTextByMap(String textId) {
return this._prepareData().then((filterd) {
return filterd[textId];
});
}
}
and the main code
import 'package:i18n/i18n.dart';
void main() {
var i18n = new I18n('../hello.json', 'EN');
i18n.getTextByMap('TEXT1').then((val) => print(val));
i18n.getTextByMap('TEXT2').then((val) => print(val));
}
Everything here, happen in dart async way, read json file etc. And everytime, when i call the method
i18n.getTextByMap('TEXT1').then((val) => print(val));
it gonna read the json file again and again. I tried to rewrite the method to prevent reading json file many times
Future<String> getTextByMap(String textId) {
if(this._jsonFiltered == null)
{
return this._prepareData().then((filterd) {
return filterd[textId];
});
}
return new Future(() => this._jsonFiltered[textId]);
}
but it doesn't work too, because dart works in async way.
My question is, how can i keep this json file content in an object? Read json file only one time and keep the contents in an object, it is better then read json file everytime, that is my opinion.
It could do everything in sync way, then i wouldn't have such as problem but this is not dart terminology.
In which order do dart execute I/O operations, like this?
Future
I/O Events
My solution would be to create a class with a factory constructor. The factory constructor always returns a object of that file.
Your problem is that futures are parallel. So both calls are executed in parallel. The solution is to let the first future complete and then do other stuff to be able to get cached results.
Then you can have a read() method that reads the value of the file if it is not present in the classes "contents" attribute for example - or if that attribute is not null, it loads the file in background.
In both cases a completer or future is returned you can listen on.
EDIT Example Code:
example_async_file_factory.dart
import 'dart:io';
import 'dart:async';
class FileHolder {
String _contents = null;
String path;
static Map<String, FileHolder> _files;
factory FileHolder(String path) {
if (_files == null) {
_files = {};
}
if (_files.containsKey(path)) {
return _files[path];
} else {
final fh = new FileHolder._internal(path);
_files[path] = fh;
return fh;
}
}
FileHolder._internal(this.path);
Future<String> getContents() {
if(_contents != null) {
print("cached");
return new Future.value(_contents);
} else {
print("read");
File f = new File(this.path);
Future<String> future = f.readAsString();
Completer completer = new Completer();
future.then((String c) {
_contents = c;
completer.complete(_contents);
});
return completer.future;
}
}
}
void main() {
FileHolder f = new FileHolder("example_async_file_factory.dart");
f.getContents().then((String contents) {
print(contents.length);
FileHolder f2 = new FileHolder("example_async_file_factory.dart");
f2.getContents().then((String contents) {
print(contents.length);
});
f2.getContents().then((String contents) {
print(contents.length);
});
f.getContents().then((String contents) {
print(contents.length);
});
});
}
Output:
read
1411
cached
cached
cached
1411
1411
1411
Regards
Robert

Resources