How do I read console input / stdin in Dart? - dart

How do I read console input from stdin in Dart?
Is there a scanf in Dart?

The readLineSync() method of stdin allows to capture a String from the console:
import 'dart:convert';
import 'dart:io';
void main() {
print('1 + 1 = ...');
var line = stdin.readLineSync(encoding: utf8);
print(line?.trim() == '2' ? 'Yup!' : 'Nope :(');
}
Old version:
import 'dart:io';
main() {
print('1 + 1 = ...');
var line = stdin.readLineSync(encoding: Encoding.getByName('utf-8'));
print(line.trim() == '2' ? 'Yup!' : 'Nope :(');
}

The following should be the most up to date dart code to read input from stdin.
import 'dart:async';
import 'dart:io';
import 'dart:convert';
void main() {
readLine().listen(processLine);
}
Stream<String> readLine() => stdin
.transform(utf8.decoder)
.transform(const LineSplitter());
void processLine(String line) {
print(line);
}

import 'dart:io';
void main(){
stdout.write("Enter your name : ");
var name = stdin.readLineSync();
stdout.write(name);
}
Output
Enter your name : Jay
Jay
By default readLineSync() takes input as string. But If you want integer input then you have to use parse() or tryparse().

With M3 dart classes like StringInputStream are replaced with Stream, try this:
import 'dart:io';
import 'dart:async';
void main() {
print("Please, enter a line \n");
Stream cmdLine = stdin
.transform(new StringDecoder())
.transform(new LineTransformer());
StreamSubscription cmdSubscription = cmdLine.listen(
(line) => print('Entered line: $line '),
onDone: () => print(' finished'),
onError: (e) => /* Error on input. */);
}

As of Dart 2.12, null-safety is enabled, and stdin.readLineSync() now returns a String? instead of a String.
This apparently has been confusing a lot of people. I highly recommend reading https://dart.dev/null-safety/understanding-null-safety to understand what null-safety means.
For stdin.readLineSync() specifically, you can resolve this by checking for null first, which for local variables will automatically promote a String? to a String. Here are some examples:
// Read a line and try to parse it as an integer.
String? line = stdin.readLineSync();
if (line != null) {
int? num = int.tryParse(line); // No more error about `String?`.
if (num != null) {
// Do something with `num`...
}
}
// Read lines from `stdin` until EOF is reached, storing them in a `List<String>`.
var lines = <String>[];
while (true) {
var line = stdin.readLineSync();
if (line == null) {
break;
}
lines.add(line); // No more error about `String?`.
}
// Read a line. If EOF is reached, treat it as an empty string.
String line = stdin.readLineSync() ?? '';
Note that you should not blindly do stdin.readLineSync()!. readLineSync returns a String? for a reason: it returns null when there is no more input. Using the null assertion operator (!) is asking for a runtime exception.

Note that while calling stdin.readLineSync() your isolate/thread will be blocked, no other Future will be completed.
If you want to read a stdin String line asynchronously, avoiding isolate/thread block, this is the way:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
/// [stdin] as a broadcast [Stream] of lines.
Stream<String> _stdinLineStreamBroadcaster = stdin
.transform(utf8.decoder)
.transform(const LineSplitter()).asBroadcastStream() ;
/// Reads a single line from [stdin] asynchronously.
Future<String> _readStdinLine() async {
var lineCompleter = Completer<String>();
var listener = _stdinLineStreamBroadcaster.listen((line) {
if (!lineCompleter.isCompleted) {
lineCompleter.complete(line);
}
});
return lineCompleter.future.then((line) {
listener.cancel();
return line ;
});
}

All these async API readLine*() based solutions miss the syntactic sugar which gives you the ability to do everything without synchronous blocking, but written like synchronous code. This is even more intuitive coming from other languages where you write code to execute synchronously:
import 'dart:convert';
import 'dart:io';
Future<void> main() async {
var lines = stdin.transform(utf8.decoder).transform(const LineSplitter());
await for (final l in lines) {
print(l);
}
print("done");
}
The key takeaway here is to make use of async and await:
async on your method is required, as you're using await to interface with asynchronous API calls
await for is the syntax for doing "synchronous-like" code on a Stream (the corresponding syntax for a Future is just await).
Think of await like "unwrapping" a Stream/Future for you by making the following code execute once something is available to be handled. Now you're no longer blocking your main thread (Isolate) to do the work.
For more information, see the Dart codelab on async/await.
(Sidenote: The correct way to declare any return value for an async function is to wrap it in a Future, hence Future<void> here.)

You can use the following line to read a string from the user:
String str = stdin.readLineSync();
OR the following line to read a number
int n = int.parse(stdin.readLineSync());
Consider the following example:
import 'dart:io'; // we need this to use stdin
void main()
{
// reading the user name
print("Enter your name, please: ");
String name = stdin.readLineSync();
// reading the user age
print("Enter your age, please: ");
int age = int.parse(stdin.readLineSync());
// Printing the data
print("Hello, $name!, Your age is: $age");
/* OR print in this way
* stdout.write("Hello, $name!, Your age is: $age");
* */
}

You could of course just use the dcli package and it's ask function
Import 'package: dcli/dcli.dart':
Var answer = ask('enter your name');
print (name);
Use the named validator argument to restrict input to integers.

To read from the console or terminal in Dart, you need to:
import 'dart:io' library
store the entered value using stdin.readLineSync()!
parse the input into an int using int.parse(input) if necessary
Code:
import 'dart:io';
void main() {
String? string;
var number;
stdout.writeln("Enter a String: ");
string = stdin.readLineSync()!;
stdout.writeln("Enter a number: ");
number = int.parse(stdin.readLineSync()!);
}

Related

How to transform a Sink<T> into a Sink<V>?

I have a method that can emit its output into a given Sink<Node>.
I wanted to pipe that into stdout which is a Sink<List<int>>.
Supposing I have a function convert that converts Node to List<int>, how can I transform stdout into a Sink<Node>, so that it will print my Tree to the console?
I have made this example showing how you can do it with a StreamController:
import 'dart:async';
import 'dart:convert';
import 'dart:io';
class Message {
String text;
Message(this.text);
}
void main() {
final controller = StreamController<Message>();
stdout.addStream(controller.stream
.map((var msg) => msg.text)
.transform(const Utf8Encoder()));
var messageSink = controller.sink;
messageSink.add(Message('Hello World'));
}
The StreamController in this example takes Message objects and converts them into List<int> by first using map to convert the Message to String object and then use a transformer to convert the String into a List of UTF8 bytes.
I've filed: https://github.com/dart-lang/sdk/issues/50607
Here is how I solved this:
class _MappedSink<From, To> implements Sink<From> {
final To Function(From) _transform;
final Sink<To> _sink;
const _MappedSink(this._sink, this._transform);
#override
void add(From data) => _sink.add(_transform(data));
#override
void close() => _sink.close();
}
extension SinkMap<To> on Sink<To> {
Sink<From> map<From>(To Function(From) transform) =>
_MappedSink(this, transform);
}

How to get console integer input from user in a dart program?

I want to get integer input from console in dart but unable to do so . How do I do that?
main()
{
int n = stdin.readLineSync();
}
You need to parse string captured by stdin.readLineSync() using int.parse()
import 'dart:io';
void main() {
int n = int.parse(stdin.readLineSync());
}

How to write a `ByteData` instance to a File in Dart?

I am using Flutter to load an "asset" into a File so that a native application can access it.
This is how I load the asset:
final dbBytes = await rootBundle.load('assets/file');
This returns an instance of ByteData.
How can I write this to a dart.io.File instance?
ByteData is an abstraction for:
A fixed-length, random-access sequence of bytes that also provides
random and unaligned access to the fixed-width integers and floating
point numbers represented by those bytes.
As Gunter mentioned in the comments, you can use File.writeAsBytes. It does require a bit of API work to get from ByteData to a List<int>, however.
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
I've also filed an issue to make the docs on Flutter more clear for this use case.
you need to have path_provider package installed, then
This should work :
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
final dbBytes = await rootBundle.load('assets/file'); // <= your ByteData
//=======================
Future<File> writeToFile(ByteData data) async {
final buffer = data.buffer;
Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var filePath = tempPath + '/file_01.tmp'; // file_01.tmp is dump file, can be anything
return new File(filePath).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
//======================
to get your file :
var file;
try {
file = await writeToFile(dbBytes); // <= returns File
} catch(e) {
// catch errors here
}
Hope this helps,
Thank you.
to search flutter ByteData to List<int> then found here, but not fully answer my question:
how to convert ByteData to List<int> ?
after self investigate, solution is:
use .cast<int>()
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.cast<int>();
or 2. use .map
ByteData audioByteData = await rootBundle.load(audioAssetsFullPath);
Uint8List audioUint8List = audioByteData.buffer.asUint8List(audioByteData.offsetInBytes, audioByteData.lengthInBytes);
List<int> audioListInt = audioUint8List.map((eachUint8) => eachUint8.toInt()).toList();
For those looking to write bytes (aka Uint8List) instead of ByteData please note that ByteData is a wrapper for Uint8List.
From /runtime/lib/typed_data.patch:
#patch
class ByteData implements TypedData {
#patch
#pragma("vm:entry-point")
factory ByteData(int length) {
final list = new Uint8List(length) as _TypedList;
_rangeCheck(list.lengthInBytes, 0, length);
return new _ByteDataView(list, 0, length);
}
#patch
class Uint8List {
#patch
#pragma("vm:exact-result-type", _Uint8List)
factory Uint8List(int length) native "TypedData_Uint8Array_new";
}
If you are using the latter type you can use the answer provided by Rami and modify the return as follow:
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:path_provider/path_provider.dart';
Future<File> writeToFile(Uint8List data) async {
(...)
return new File(filePath).writeAsBytes(data);
}

How do I capture stdin (more than one line) to a String in Dart?

I have a Dart command-line program that want to be able to pipe data from the shell to the Dart program (e.g., cat file.txt | dart my_program.dart or accept input until the user uses Ctrl+d). Going through the tutorials online, the only documentation I found on saving input from the stdin was stdin.readLineSync(). However, as the name implies, this only reads the first line.
How can capture the entire contents of the stdin to a String? Also, would there be any security concerns if a user tries to pipe in an enormously large file? Is there a limit to how long the String can be? How can I safeguard against that?
Thanks for your help!
The following program if used interactively will echo your input but capitalise each character.
You can also pipe a file to it.
dart upper_cat.dart < file.txt
This will output the file with every character capitalised.
import 'dart:convert';
import 'dart:io';
main() {
// Stop your keystrokes being printed automatically.
stdin.echoMode = false;
// This will cause the stdin stream to provide the input as soon as it
// arrives, so in interactive mode this will be one key press at a time.
stdin.lineMode = false;
var subscription;
subscription = stdin.listen((List<int> data) {
// Ctrl-D in the terminal sends an ascii end of transmission character.
// http://www.asciitable.com/
if (data.contains(4)) {
// On my computer (linux) if you don't switch this back on the console
// will do wierd things.
stdin.echoMode = true;
// Stop listening.
subscription.cancel();
} else {
// Translate character codes into a string.
var s = LATIN1.decode(data);
// Capitalise the input and write it back to the screen.
stdout.write(s.toUpperCase());
}
});
}
There is also the console library to help with this kind of stuff. I haven't tried it, but give it a shot and report back ;)
The following example handles UTF8 input - the example above requires 1 byte characters as input.
import 'dart:convert';
import 'dart:io';
main() {
stdin.echoMode = false;
stdin.lineMode = false;
var subscription;
subscription = stdin
.map((List<int> data) {
if (data.contains(4)) {
stdin.echoMode = true;
subscription.cancel();
}
return data;
})
.transform(UTF8.decoder)
.map((String s) => s.toUpperCase())
.listen(stdout.write);
}
I studied the code of the stdin.readLineSync() and was able to modify it to fit my needs:
import 'dart:convert';
import 'dart:io';
String readInputSync({Encoding encoding: SYSTEM_ENCODING}) {
final List input = [];
while (true) {
int byte = stdin.readByteSync();
if (byte < 0) {
if (input.isEmpty) return null;
break;
}
input.add(byte);
}
return encoding.decode(input);
}
void main() {
String input = readInputSync();
myFunction(input); // Take the input as an argument to a function
}
I needed to read synchronously from the stdin in order to pause the program until the entire stdin (until the end of file or Ctrl+d) was read.
Thanks for all your help! I don't think I would have been able to figure it out without your help.
You can use
import 'dart:io' as io;
import 'dart:async' show Future, Stream, StreamSubscription;
import 'dart:convert' show UTF8;
void main() {
StreamSubscription subscr = io.stdin.transform(UTF8.decoder).listen((data) =>
print(data));
}
You can control whether more data should be received or not with
subscr.pause();
subscr.resume();
Listen on stdin passes data in chunks of 16kb if I remember correctly (io buffer size defined by Dart) this allows you to receive the amount of data you can process.
If you want to keep one big string in memory you need to have that memory available.
See also
- https://stackoverflow.com/a/26343041/217408 (set VM memory)
- https://stackoverflow.com/a/12691502/217408
- https://stackoverflow.com/a/28039661/217408

What is the difference between Stream<List<int>> and Stream<int> in Dart

I am trying to wrap my head around Dart Streams. In particular this example of the command line utility cat has the following lines of code:
Stream<List<int>> stream = new File(path).openRead();
// Transform the stream using a `StreamTransformer`. The transformers
// used here convert the data to UTF8 and split string values into
// individual lines.
return stream
.transform(UTF8.decoder)
.transform(const LineSplitter())
.listen((line) {
if (showLineNumbers) {
stdout.write('${lineNumber++} ');
}
stdout.writeln(line);
}).asFuture().catchError((_) => _handleError(path));
The declaration of the Stream<T> as Stream<List<int>> has me a bit confused. Why is it not declared as a Stream<int>. How does the List<> type make this different. Are the subscriber events buffered in some way if it is a List?
What Type (as in <T>) is passed to the first transform? Is it an int or a List<int>?
What type is passed to each of the next transforms and what determines their type.
Does this example read the entire file before passing the results of the transform to the next transform? If so, is there an example somewhere of how to Stream very large files similar to this Node question Parsing huge logfiles in Node.js - read in line-by-line
Good question.
UTF8 is a Utf8Codec that extends Codec<String, List<int>>. So UTF8.decoder is a Converter<List<int>, String> that takes List<int> as parameter.
LineSplitter is a Converter<String, List<String>>. So it takes String as parameter. The resulting stream of .transform(const LineSplitter()) is a Stream<String> where each line is sent.
File.openRead doesn't read the entire file before writing the first bytes to the stream. So there's no problem to deal with large files.
Alexandre Ardhuin has the first three questions right. The 4th question however is not. After taking this apart and stubbing out my own version of the code I determined the following:
Even on a 37Mb file, the the transforms only get called once.
Here is the code I used to figure it out.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
void main(List<String> arguments) {
Stream<List<int>> stream = new File('Data.txt').openRead();
stream
.transform(const Utf8InterceptDecoder())
.transform(const LineSplitterIntercept())
.listen((line) {
// stdout.writeln(line);
}).asFuture().catchError((_) => print(_));
}
int lineSplitCount = 0;
class LineSplitterIntercept extends LineSplitter {
const LineSplitterIntercept() : super();
// Never gets called
List<String> convert(String data) {
stdout.writeln("LineSplitterIntercept.convert : Data:" + data);
return super.convert(data);
}
StringConversionSink startChunkedConversion(ChunkedConversionSink<String> sink) {
stdout.writeln("LineSplitterIntercept.startChunkedConversion Count:"+lineSplitCount.toString()+ " Sink: " + sink.toString());
lineSplitCount++;
return super.startChunkedConversion(sink);
}
}
int utfCount = 0;
class Utf8InterceptDecoder extends Utf8Decoder {
const Utf8InterceptDecoder() : super();
//never gets called
String convert(List<int> codeUnits) {
stdout.writeln("Utf8InterceptDecoder.convert : codeUnits.length:" + codeUnits.length.toString());
return super.convert(codeUnits);
}
ByteConversionSink startChunkedConversion(ChunkedConversionSink<String> sink) {
stdout.writeln("Utf8InterceptDecoder.startChunkedConversion Count:"+ utfCount.toString() + " Sink: "+ sink.toString());
utfCount++;
return super.startChunkedConversion(sink);
}
}

Resources