Создание консольных приложений

23 March 2014 г. 19:25:29

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

Запуск приложений используя отдельную виртуальную машину Dart (Dart VM)

Краткий обзор кода приложения dcat

Обработка аргументов командной строки

Чтение и запись используя stdin, stdout и stderr

Получение данных о файле

Чтение файла

Запись файла

Получение информации об окружении

Настройка кода выхода

Итоги

Запуск приложений используя отдельную виртуальную машину Dart (Dart VM)

Для запуска приложения в командной строке вам понадобится Dart VM (dart), который входит в Dart SDK. (Если Вы скачали Dart Editor, то у вас уже есть виртуальная машина Dart)

Если вы устанавливаете скаченный Dart в директорий под названием ~/myDartDownload, Вы найдете Dart в ~/myDartDownload/dart-sdk/bin

Директория Dart VM

Поместив этот директорий в PATH вы можете использовать команду dart, а так-де другие команды используя имя, например dart analyzer.

Давайте запустим небольшую программу.

  1. Создайте файл под названием helloworld.dart содержащий следующий код:

    void main() { print('Hello, World!'); }

  2. Запустите программу при помощи командной строки в директории, в которой находится файл, который Вы только что создали, как показано выделенным текстом ниже.

    % dart helloworld.dart Hello, World! %

Виртуальная машина Dart поддерживает множество опций. Чтобы посмотреть общую информацию об опциях, используйте dart --help. Чтобы посмотреть все опции, используйте dart --verbose.

Краткий обзор кода приложения dcat

Взглянем на код небольшого приложения под названием dcat, которое отображает содержание любые перечисленные файлы в командной строке. Эта программа использует различные классы, функции и доступные в консольным приложениям свойства. В следующей части данной статьи речь пойдет об деталях этого приложения. Наведите курсор над выделенным текстом ниже, для краткого обзора.

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:args/args.dart';

const LINE_NUMBER = 'line-number';
var NEWLINE = '\n';

ArgResults argResults;

void main(List arguments;) {
  final parser = new ArgParser()
      ..addFlag(LINE_NUMBER, negatable: false, abbr: 'n');

  argResults = parser.parse(arguments);
  List<String> paths = argResults.rest;

  dcat(paths, argResults[LINE_NUMBER]);
}

Future dcat(List<String> paths, bool showLineNumbers) {
  if (paths.isEmpty) {
    // No files provided as arguments. Read from stdin and print each line.
    return stdin.pipe(stdout);
  } else {
    return Future.forEach(paths, (path) {
      int lineNumber = 1;
      Stream<List> stream = new File(path).openRead();
      return stream
          .transform(UTF8.decoder);
          .transform(const LineSplitter())
          .listen((line); {
            if (showLineNumbers) {
              stdout.write('${lineNumber++} ');
            }
            stdout.writeln(line);
          }).asFuture().catchError((_) => _handleError(path));
    });
  }
}

_handleError(String path) {
  FileSystemEntity.isDirectory(path);.then((isDir) {
    if (isDir) {
      print('error: $path is a directory');
    } else {
      print('error: $path not found');
    }
  });
  exitCode = 2;
}

Обработка аргументов командной строки

Пакет args является комплектом программ, содержащие библиотеки Dart, которые предоставляют обработку для преобразования сырых аргументов командной строки в набор опций, флагов и дополнительных значений. Импорт библиотеки показан ниже:

import 'package:args/args.dart';

Аргументы библиотеки содердат два класса:

БиблиотекаОписание
ArgParser Класс обрабатывающий аргументы командной строки
ArgResults Результат обработки аргументов командной строки при помощи ArgParser

Давайте взглянем на пример dcat использующий для обработки и сохранения аргументов командной строки ArgParser и ArgResults.

  1. Скопируйте пример из репозитория гитхаба: dcat.dart

  2. Запустите программу из командной строки как показано выделенным текстом ниже.

    $ dart dcat.dart -n quotes.txt 1 Be yourself. Everyone else is taken. -Oscar Wilde 2 Don't cry because it's over, smile because it happened. -Dr. Seuss 3 You only live once, but if you do it right, once is enough. -Mae West ...

Программа выведет содержание исходного файла и присвоит каждой строке порядковый номер.

Обработка аргументов командной строки

Диаграмма ниже показывает как в dcat аргументы командной строки преобразуются в объект ArgResult.

Вы можете обращаться к флагам и опциям по имение, так как объект ArgResult представляется в виде словаря.

Вы можете обращаться к другим значениям используя различные свойства, например rest.

Вот часть кода приложения dcat которая имеет дело с обработкой параметров командной строки:

...
ArgResults argResults;

void main(List arguments) {
  final parser = new ArgParser()
      ..addFlag(LINE_NUMBER, negatable: false, abbr: 'n');

  argResults = parser.parse(arguments);
  List<String> paths = argResults.rest;

  dcat(paths, argResults[LINE_NUMBER])
}
...

Больше информации об использовании классов ArgsParser и ArgResults для обработки аргументов, Вы можете получить в документации API.

Чтение и запись используя stdin, stdout и stderr

Как и в других языках, в Dart есть стандартные патоки ввода, вывода и обработки ошибок. Стандартные потоки ввода/вывода определяют верхний уровень библиотеик dart.io.

ПотокОписание
stdout Стандартный ввод
stderr Стандартный поток ошибок
stdin Стандартный поток ввода

Импорт библиотеки dart:io показан ниже:

import 'dart:io';

Библиотеку dart:io могут использовать только консольные приложения, но никак не веб приложения.

stdout

Вот код программы dcat которая выводит номера строк в stdout (если передан флаг -n) и далее саму строки из файла.

if (showLineNumbers) {
  stdout.write('${lineNumber++} ');
}
stdout.writeln(line);

Методы write() и writeln() принимают объекты любого типа, преобразуют их в строки и выводят. Метод writeln() выводит в конце символ перехода на новую строку. dcat использует метод write() для вывода номера строки, так номер строки и текст должны выводиться в одной и той-же строке.

Вы так же можете использовать метод writeAll() для вывода списка объектов, или используя addStream() для асинхронного вывода всех элементов в поток.

stdout предоставляет большую функциональность чем функция print(). Например при помощи stdout, Вы можете выводить содержание потоков. Однако Вы должны использовать print() вместо stdout в программах которые будут конвертироваться и выполняться в JavaScript

stderr

Ипользуйте stderr для вывода в консоль сообщений об ошибках. Стандартный поток ошибок содержит такие же методы как и stdout и используются они аналогичным образом. И stdout и stderr выполняют вывод в консоль, их выходные данные разделяются и может быть перенаправлена в командной строке или программно в другое назначение.

Этот код dcat выводит сообщение об ошибке если пользователь пытается вывести директорий, или файл не существует.

if (isDir) {
  stderr.writeln('error: $path is a directory');
} else {
  stderr.writeln('error: $path not found');
}

stdin

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

Вот небольшой пример которые читает одну строку при помощи stdin:

import 'dart:io';

void main() {
  stdout.writeln('Type something');
  String input = stdin.readLineSync();
  stdout.writeln('You typed: $input');
}

Метод readLineSync() читает текст из стандартного потока ввода, блокируя программу пока пользователь не введет текст и не нажмет ввод. Это маленькая программа выводит напечатанный текст.

Если пользователь не предоставит программе dcat в командной строке имя файла, программа прочитает синхронно из stdin используя метод pipe().

return stdin.pipe(stdout);

В этом случае, пользователь введет строку текста и программ скопирует его при помощи stdout. Пользователь заканчивает ввод вводя <ctrl+d>.

$ dart dcat.dart
The quick brown fox jumped over the lazy dog.
The quick brown fox jumped over the lazy dog.
...

Получение данных о файле

Класс FileSystemEntity библиотеки dart:io предоставляет свойства и статические метода, которые помогут вам просматривать и изменять файловойю систему.

Например, если у вас есть путь то, при помощи метода type() класса FileSystemEntity, Вы можете определить является ли путь файлом, директорией, ссылкой или файл не найден. Потому что метод type() имеет доступ к файловой системе и производит проверку асинхронно при помощи фьючерса.

Следующий код из примера dcat использует FileSystemEntity для определения является ли предоставленный в командной строке путь директорией. Фьючерс возвращает булевское значение которое показывает является путь директорией или нет.

FileSystemEntity.isDirectory(path).then((isDir) {
  if (isDir) {
    stderr.writeln('error: $path is a directory');
  } else {
    stderr.writeln('error: $path not found');
  }
  exit(2);
});

Еще несколько интересных методов класса FileSystemEntity isFile(), exists(), stat(), delete() и rename(). Все они тоже возвращают в качестве значения фьючерс.

FileSystemEntity является суперклассом для классов File, Directory и Link.

Чтение файла

Приложение dcat открывает каждый файл из списка при помощи метода openRead(), который возвращает поток. Метод listen() регистрирует колбэк функцию которая выполняется когда данные получены в потоке. Эта колбэк функция записывает данные в stdout.

return Future.forEach(paths, (path) {
  int lineNumber = 1;
  Stream<List<int>> stream = new File(path).openRead();

  return stream
      ...
      .listen((line) {
        if (showLineNumbers) {
          stdout.write('${lineNumber++} ');
        }
        stdout.writeln(line);
      }).asFuture()
          .catchError((_) => _handleError(path));
});

Далее показана оставшаяся часть кода, которая использует два декодера, которые преобразуют данные до вызова функции listen(). Декодер UTF* конвертирует данные в строки Dart. LineSplitter делит данные на строки.

return Future.forEach(paths, (path) {
  int lineNumber = 1;
  Stream<List<int>> stream = new File(path).openRead();

  return stream
      .transform(UTF8.decoder)
      .transform(const LineSplitter())
      .listen((line) {
        if (showLineNumbers) {
          stdout.write('${lineNumber++} ');
        }
        stdout.writeln(line);
      }).asFuture()
          .catchError((_) => _handleError(path));
});

Эти функции по преобразованию содержит библиотека dart:convert , включая функцию для JSON. Для использования этих конвертеров Вам надо выполнить импорт библиотеки dart:convert:

import 'dart:convert';

Запись в файл

Самый простой способ записи текста в файл это создание объекта File и использование метода writeAsString():

File quotesFile = new File('quotes.txt');
String stronger = 'That which does not kill us makes us stronger. -    Nietzsche';

quotesFile.writeAsString(stronger, mode: FileMode.APPEND)
    .then((_) { print('Data written.'); });

Метод writeAsString() записывает данные асинхронно используя фьючерс. Он открывает файл до записи и закрывает файл когда все сделано. Для добавления данных в существующий файл, Вы можете использовать опциональный параметр mode и задать его значение в FileMode.APPEND. По умолчанию mode установлен в FileMode.WRITE и предыдущее содержание файла, если оно было, пере запишется.

Если Вы хотите записать много данных, Вы можете открыть файл на запись. Метод openWrite() возвращает IOSink, (того же типа как stdin или stderr). Вы можете продолжать запись, а когда закончите должны закрыть файл. Метод close() асинхронный и возвращает фьючерс.

IOSink quotes = new File('quotes.txt').openWrite(mode: FileMode.APPEND);

quotes.write('A woman is like a tea bag; ');
quotes.write("you never know how strong it is until it's in hot water.");
quotes.writeln(" -Eleanor Roosevelt");
quotes.close().then((_) { print('done'); } );

Получение информации об окружении

Для получения информации о машине или операционной системе в которой программа запущена используется класс Platform. Замечание: необходимо использовать класс Platform из библиотеки dart:io, а не из dart.html.

Platform.environment предоставляет копию переменных среды в виде неизменяемого словаря. Если Вам необходим изменяемый словарь Вы можете использовать Map.from(Platform.environment).

Map environmentVars = Platform.environment;

print('PWD = ${environmentVars["PWD"]}');
print('LOGNAME = ${environmentVars["LOGNAME"]}');
print('PATH = ${environmentVars["PATH"]}');

Platform предоставляет и другие полезные свойства которые дают информацию о машине, операционной системе или запущенной программе. Например:

  • Platform.isMacOS()
  • Platform.numberOfProcessors
  • Platform.script.path

Настройка кода выхода

Библиотека dart:io определяет свойства верхнего уровня exitCode, которое Вы можете изменять чтобы указать код выхода для текущего вызова Dart VM. Код выхода передается в виде числа из программы Dart в родительский процесс чтобы передать сигнал об успешном или не успешном завершении или передать другое состояние выполнения программы.

Программа dcat устанавливает код выхода в функции _handleError() для сообщения о том, что произошла ошибка при выполнении.

_handleError(String path) {
  FileSystemEntity.isDirectory(path).then((isDir) {
    if (isDir) {
      stderr.writeln('error: $path is a directory');
    } else {
      stderr.writeln('error: $path not found');
    }
    exitCode = 2;
  });
}

Код выхода 2 сигналисирует о том что при выполнении произошла ошибка.

Другой способ использования exitCode это использование функции верхнего уровня exti(), которая устанавливает код выхода и немедленно выходит из программы. Например функция _handleError может вызывать вместо exitCode exit(2), однако exit() прерывает выполнение программы и возможно не обработает все файлы в списке.

*В общем, лучше использовать свойство exitcode, которое указывает код но позволяет программе продолжится и завершиться естественным путем. Таким образом вы можете использовать любой код завершения. Таблица допустимых кодов представлена ниже:

кодЗначение
0 Успешно
1 Предупреждение
2 Ошибки

Итоги

В этой статье описаны некоторые базовые возможности API. Все классы находятся в библиотеке dart:io:

APIОписание
IOSink Вспомогательный класс для объектов, которые обрабатывают данные из потоков.
File Класс представляет файл в виде файла в файловой системе
Directory Представляет директорий в файловой системе
FileSystemEntity Суперкласс для File и Directory
Platform Предоставляет информацию о машине и операционной системе
stdout Стандартный вывод
stderr Стандартные ошибки
stdin Стандартный ввод
exitCode Устанавливает код завершения
exit() Устанавливает код завершения и выходит

В дополнение эта статья охватывает два класса которые помогают в обработке параметров командной строки:

КлассОписание
ArgParser Класс преобразующий сырой список аргументов в набор опций и флагов
ArgResults Результат обработки аргументов командной строки используя ArgParser

Оставьте свой комментарий

comments powered by Disqus
Меню

Cult of digits 2014 Яндекс.Метрика