This work is licensed with Creative Commons Attribution 3.0 Unported License. You are able: - to Share — to copy, distribute and transmit the work - to Remix — to adapt the work Автор: Leonid Krashenko e-mail: Leonid.Krashenko@gmail.com Перевод данного руководства - одна из тем атома Org1 (орг ван), см. http://trac.assembla.com/org1/wiki/WikiStart = Глава 2. Консольный ввод-вывод = Существует, в основном, 4 способа отправить что-нибудь в консоль с помощью Tango: * Используя '''C printf''' (не рекомендуется, обычно используют для отладки); * Используя '''Cout/Cerr''', которые не отягощены зависимостями и компилируются в небольшой код, однако они бедны возможностями форматирования; * Используя '''Stdout/Stderr''' - стандартный путь. Это аналог '''printf''' для Tango. * Используя '''Trace''', которая суть приспособленый для работы с несколькими потоками аналог Stderr. Этот вариант полезен при отладке. Следующая диаграмма отражает типы объектов, работающих с консолью: http://www.dsource.org/projects/tango/browser/trunk/doc/images/objdia.tango.console.png == Cout/Cerr == С их помощью можно вывести текст в консоль с минимальными зависимостями. Они - суть предопределенные сушности пакета {{{tango.io.Console}}}, которые принимают в качестве аргумента массив char[] и направляют его в соответствующее устройство вывода: {{{ import tango.io.Console; Cout ("now is the time for all good men to come to the aid of their country").newline; }}} Заметим, что во второй строке добавлен модификатор *newline*. Разрывы строк могут быть встроены прямо в текст с использование традиционного синтакса *\n*. Консольный вывод буферизован: без модификатора newline текст не будет сразу же послан в пункт назначения. Там, где разрывы строк неприемлемы, немедленный вывод может быть достингут с модификатором flush: {{{ Cout ("now is the time for all good men to come to the aid of their country").flush; }}} Методы пакета Console возвращают !!!chaining ссылку на себя, позволяя применять следующий синтакс: {{{ Char[] people = "women"; Cout ("now is the time for all good ") (people) (" to come to the aid of their country").newline; }}} Заметим также, что все аргументы имеют тип char[] и выводятся в порядке слева-направо. (...). Вместо модификатора flush можно воспользоваться сокращением: {{{ Cout ("What is your name? ") (); }}} Ссылки на объекты также могут быть переданы в Cout: {{{ auto o = new Object; Cout ("the name of Object is ")(o).newline; }}} Console пользуется объектами stream с целью улучшить выразительные возможности. Например, сокращенно копирование текстового файла в консоль может быть записано следующим образом: {{{ auto file = new FileConduit ("myfile"); Cout.stream.copy (file); }}} Аналогично, мы могли бы скопировать содержимое веб-сайта (используя путь /index.html): {{{ auto site = new SocketConduit; site.connect (new InternetAddress("myWebSite")).write ("GET /index.html HTTP/1.0\n\n"); Cout.stream.copy (site); }}} Консольный ввод обрабатывается подобным образом, однако с использованием предопределенной сущности Cin. Танго ожидает, когда на входе появится что-нибудь, и возвращает все это вызывающему. В случае с интерактивным вводом, обычно это одна строка. Например: {{{ Cout ("What is your name? ") (); auto name = Cin.get(); Cout ("Hello ") (name).newline; }}} == Cin == Cin - это поток ввода. В случае, если истичник - текст, набираемый в консоли, этот поток всегда ориентирован на строки. Это означает, что ОС всегда поджидает, когда пользователь нажмет клавишу "Ввод", после чего передает всю строчку приложению. Если последнее ожидает, когда нажмется какая-то определенная клавиша или каких-нибудь ее особенных состояний, ему потребуется использовать функции Си из пакета tango.stdc. Cin может считывать строку с помощью readln, которая возвращает часть внутреннего буфера, либо с помощью copyln, которая возвращает копию. Когда применяется перенаправление, Cin будет транслировать большие порции перенаправленного ввода вызывающей стороне при каждом вызове. Одним из способов соединить ввод в целые строки текста мог бы быть потоково-ориентированный итератор: {{{ import tango.text.stream.LineIterator; foreach (line; new LineIterator!(char)(Cin.stream)) // делать что-нибудь с каждой строкой }}} Здесь продемонстрировано использование консольного ввода как потока. Последующие релизы библиотеки могут включать такую функциональность, если пользователи сочтут ее подходящей. Весь консольный ввод-вывод строго в UTF-8. Это необходимо для обеспечения кроссплатформенности, а также для того, чтобы быть уверенным, что все консольные перенаправления обрабатываются должным образом во всех случаях. == Исключения == Исключения ввода-вывода возникают в тех случаях, когда ОС определяет, что произошла ошибка. Это может случиться, если, скажем, перенаправление не сработало в случае с удаленным файлом. == Stdout/Stderr == Stdout для Танго - это примерно тоже самое, что {{{System.out.printf}}} для Java или printf для C: это стандартный способ напечатать форматированный вывод в консоль. Stdout - это форматёр (formatter) общего назначения, базирующийся на Cout. Существует также Stderr, связанный с Cerr; оба они предопределены внутри {{{tango.io.Stdout}}}. В то время как Cout обрабатывает только char[], Stdout поддерживает широкий спектр типов, преобразуя их из родного представления в текст, а также конвертируя текст, представленный в UTF-16 или UTF-32. Основная функциональность обеспечивается модулем {{{tango.text.convert.Format}}} и продемонстрирована несколькими удобными методами. Подробнее о форматировании читайте [TangoChapterConversions#Layoutsformatstring здесь]. Эти методы возвращают !!!chaining ссылку на самих себя (как большинство объектов библиотеки) и принимают множества аргументов в стиле {{{vararg}}}, например: {{{ import tango.io.Stdout; Stdout (10) (" green bottles, sitting on the wall").newline; }}} в стиле {{{vararg}}} выражается так: {{{ Stdout.format ("{} green bottles, sitting on the wall", 10).newline; }}} Также есть возможность включить разрыв строки: {{{ Stdout.formatln ("{} green bottles, sitting on the wall", 10); }}} Немедленный вывод всего, что накопилось в буфере, без использования разрыва строки произойдет аналогично Cout: либо с использованием модификатора {{{.flush}}}, либо - пустых скобок: {{{ Stdout (10) (" green bottles, sitting on the wall") (); }}} Подобно Cout и Cerr, Stdout и Stderr косвенно используют {{{OutputStream}}}, который может быть использован напрямую при необходимости. Например, в примере с Cout: {{{ auto file = new FileConduit ("myfile"); Stdout.stream.copy (file); }}} Или для избежания преобразования типов, добавляя текст прямо в буфер: {{{ Stderr.stream.write ("my error message"); }}} Подробнее о форматировании можно также прочитать в документации к {{{tango.text.convert.Format}}}. Отметим, что функциональность Stdout обеспечивается классом Print, который можно использовать для того, чтобы связать такой же интерфейс с потоком, отличающимся от тех, которые используют Cout и Cerr (например, для файла или соединения по сети). Stdout также использует объект Layout, к которому можно обратиться непосредственно. Например, иногда может быть полезно создать массив форматированного вывода: {{{ auto string = Stdout.layout.sprint ("{} green bottles", 10); }}} Также возможно настроить Stdout/Stderr для специфичной локали, заменив объект Layout на соответствующий. Это обеспечивает большую гибкость в настройке регионального форматирования представлений чисел, валюты и времени. === Исключения Stdout === Исключения могут возникнуть, если переданный аргумент не может быть обработан форматёром. == Trace == Trace не является частью {{{tango.io}}}, но он так или иначе относится к данной главе. Иногда требуется вывести что-нибудь при трассировке программы. Stdout/Stderr/Cout/Cerr не годятся для этого, т.к. они не безопасны с точки зрения многопоточности. При выводе одновременно из нескольких потоков вам стоит использовать Trace, которая адаптирована для этой ситуации. {{{ import tango.util.log.Trace; void f(){ // ... int idx = someCalc(); Trace.formatln ("{}@{} : My variable={}", __FILE__, __LINE__, idx ); // ... } }}} == C printf == Как и все стандартные функции Си, доступен и printf. Его можно использовать через модуль {{{tango.stdc.stdio}}}. {{{ import tango.io.stdio; printf ("at pos %lld, %.*s \n", __LINE__, __FILE__ ); }}} А настоящий момент массивы Ди представлены как длина+указатель на данные. Зная об этом внутреннем представлении, можно использовать printf для вывода строк с применением форматирующего спецификатора {{{"%.*s"}}}. Однако, было объявлено, что такое представление может измениться в будущем, поэтому такой способ в будущем может не работать. Особенное внимание должно быть уделено при использовании правильного спецификатора размера для printf. Т.е. {{{__LINE__}}} в Ди имеет тип long, поэтому надо писать "%lld". '''Внимание: использование printf не рекомендуется. Используйте его только при низкоуровневой отладке и только тогда, когда вы знаете, что делаете.''' == ЧаВо == Вопрос: как мне перенаправить консольный вывод? Ответ: можно переназначить соответствующий поток. Можно перенаправить низкий уровень (Cout/Cerr) или только форматированный вывод (Stdout/Stderr): {{{ Cout.output( new FileOutput("redirected")); Stdout.stream( new FileOutput("redirected")); }}} Вопрос: как сделать, чтобы программа ожидала нажатия клавиши "Ввод"? Ответ: используйте {{{Cin.get()}}}, который прочитает очередную строку из Cin, и нажатие клавиши "Ввод" даст пустую строку. Если надо проверить нажатие конкретной клавиши, используйте функции Си {{{kbhit}}} и {{{getc}}}.