Let's suppose that we have a number of text files we want to glue together in one big file. This recipe with code in the project concat_files
shows you how this can be done.
The program is launched from the command line in the bin
folder (or in Dart Editor with a Managed Launch with Script
arguments file1.txt
file2.txt file.txt
) as dart co
ncat.dart
file1.txt file2.txt file.txt
, where file1.txt
and file2.txt
are the files to be concatenated (there can be two or more files) into file.txt
. The following is the code to perform this:
import 'dart:io'; import 'package:args/args.dart'; ArgResults argResults; File output; void main(List<String> arguments) { final parser = new ArgParser(); argResults = parser.parse(arguments); final outFile = argResults.rest.last; List<String> files = argResults.rest.sublist(0, argResults.rest.length - 1); if (files.isEmpty) { print('No files provided to concatenate!'), exit(1); } output = new File(outFile); if (output.existsSync()) { output.delete(); } concat(files); } concat(List<String> files) { for (var file in files) { var input = new File(file); try { var content = input.readAsStringSync(); content += " "; output.writeAsStringSync(content, mode: FileMode.APPEND); } catch (e) { print("An error $e occurred"); } } }
We use the args
package to get the output file name and the files to concatenate. To start with an empty output file, we delete it when it already exists. Then, we loop over all the input files, successively reading an input file and write it to the output in the append mode. We do all these operations in the synchronous mode, because we don't want the content of the files to be mingled.
In concat2.dart
, which you can find within the concat_files
folder, we see an asynchronous version that also works here—only the code in the concat
method has to change. Have a look at the following code:
IOSink snk; Future concat(List<String> files) { snk = output.openWrite(mode: FileMode.APPEND); return Future.forEach(files, (file) { Stream<List<int>> stream = new File(file).openRead(); return stream.transform(UTF8.decoder) .transform(const LineSplitter()) .listen((line) { snk.write(line + " "); }).asFuture().catchError((_) => _handleError(file)); }); } _handleError(String file) { FileSystemEntity.isDirectory(file).then((isDir) { if (isDir) { print('error: $file is a directory'), } else { print('error: $file not found'), } }); }
We write to an IOSink
object snk
using the openWrite
method in the append mode. The Future.forEach
method asynchronously runs the callback provided on each file. The forEach
method runs the callback for each element in order, moving to the next element only when the Future returned by the callback completes. The stream is transformed; transformers are used here to convert the data to UTF-8 and split string values into individual lines.
args
package, and the Transforming streams recipe in this chapter.