Есть ли способ форматировать текстовый вывод в Ada
есть ли способ форматировать выводимую строку? Я пытаюсь получить довольно представление о следующем выходе
1: Ashley | 01033438392 | Wellington, New Zealand | 1987- 4-14
2: Aloha | 01087651234 | Hawaii, United States of America | 1988- 9-23
3: Jack | 01082840184 | Beijing, China | 1989- 6-19
если бы я программировал на C, я бы сделал что-то вроде
printf("%10s | %11s | %20s | %4d-%2d-%2dn",name,phone,address,year,month,day);
можно ли сделать такое форматирование в Ada 05?
PS пожалуйста, просто игнорируйте имена, номера телефонов, адрес и дату рождения. Я придумал их за 30 секунд...
6 ответов
Это можно сделать, но механизмы немного громоздким и довольно многословно.
Обычно я пишу отдельные процедуры для обработки более сложных выходных данных, например даты, и использую их с остальной обработкой строк для ясности.
package Integer_IO is new Ada.Text_IO.Integer_IO (Integer);
procedure Output_Date ( Day : in Integer; Month: in Integer; Year: in Integer) is
begin
Integer_IO.Put(Item => Day, Width => 2);
Text_IO.Put("-");
Integer_IO.Put(Item => Month, Width => 2);
Text_IO.Put("-");
Integer_IO.Put(Item => Year, Width => 4);
end Output_Date;
procedure Output_String ( Item : in String;
Width : in Integer;
Separator : in String := "|";
Truncate : Boolean := False) is
Field_Index : Integer := Text_IO.Col;
begin
if Item'length > Width and Truncate then
Text_IO.Put(Item(1..Width) & Separator);
else
Text_IO.Put(Item) & Separator;
end if;
Text_IO.Set_Col ( Field_Index + Width + 1 );
end Output_String;
это приведет к введению полей фиксированной длины, которые при необходимости позволят усечь длинные строки или переместить последующие записи в следующую строку. Задает тип будет установить положение линии напишите, потенциально поместив его на следующую строку, если текущая позиция записи уже превысила запрошенную.
Я бросил усечение строки там как шанс использовать срезание массива и манипуляцию выводом Text_IO, но я обычно не поклонник усечения по умолчанию, так как, позволяя строке переполнять запрошенную ширину или отступ на следующей строке, как правило, делают ошибки форматирования более очевидными.
Итак, распечатайте что-то вроде вашей первой строки, учитывая код выше, может выглядеть примерно так:
Name : String := "Ashley"
Phone : String := "01033438392"
Address: String := "Wellington, New Zealand"
Day : Integer := 14;
Month : Integer := 4;
Year : Integer := 1987;
Output_String(Item=> Name, Width => 10);
Output_String(Item=> Phone, Width => 11);
Output_String(Item=> Address, Width => 20);
Output_Date(Day,Month,Year);
текст IO в Ada обычно громоздкий, но обычно имеет преимущество сделать то, что вы делаете относительно ясно.
обратите внимание, что в C++ в эти дни printf()
находится на грани амортизации, в пользу использования потоков с форматерами потоков. Это удобно, но массово небезопасно (по крайней мере в паре смыслов этого слова). В наши дни разработчикам рекомендуется использовать потоки C++ (с их разнообразными манипуляторами) вместо этого.
в Ada вы можете манипулировать строками в очень похожем стиле на потоки C++ с помощью оператора catenation&
где люди C++ используют вставку потока оператор (<<
). В некотором смысле метод Ada лучше, потому что вы можете вложить catenated выражения, которые вы не можете сделать с выражениями, вставленными в поток.
проблема здесь заключается в том, что нет никаких удобный эквиваленты в C++ форматирования типа setfill()
, hex
и setw()
. Там действительно должно быть, и (hex
исключено) их не сложно написать самому, но пока они не существуют.
например, a setw()/setfill()
эквивалент будет чем-то например:
Fill_Char : Character := ' ';
function Set_Fill (New_Fill : Character) return String is
begin
Fill_Char := New_Fill;
return "";
end Set_Fill;
--// Dumb tail-recursive implementation.
function Set_Width(Source : in String; Width : in Positive) return String is
begin
if Width <= Source'length then --'
return Source;
else
return Fill_Char & Set_Width(Source, Width - 1);
end if;
end Set_Width;
Unfilled_String : constant String := "123456";
Filled_String : constant String := Set_Width(Unfilled_String & Set_Fill('0'), 8);
--// The above string should end up being "00123456"
если вы действительно хотите printf()
интерфейс printf()
вполне вызывается из Ada, конечно. Вы должны беспокоиться о переходе между строками размера Ada и строками с нулевым завершением, но это то, что Ada.Interfaces.C.Strings
там.
Да есть. Хотя это не так просто, как в c.
посмотреть §A. 4.4 Обработка Строк Ограниченной Длины для создания строк предопределенного размера и использования integer'Image для преобразования чисел. Оператор & полезен для объединения строк и вывода с помощью ada.text_io.put_line().
вам может понравиться этот простой моделирование карточной игры использует Ada.Strings.Fixed
формат оси меток ASCII-граф. См.function Label
, который использует Tail
и Trim
в формате Integer'Image
of a Lower
и Upper
значение.
код:
function Label (J : Integer) return String is
use Ada.Strings; use Ada.Strings.Fixed;
Lower : String := Integer'Image(J * Bin_Size);
Upper : String := Integer'Image((J + 1) * Bin_Size);
begin
return Tail(Trim(Lower, Left), 4, '0') & "-" &
Tail(Trim(Upper, Left), 4, '0') & " |*";
end Label;
есть вспомогательные инструменты для этой конкретной установки формата.
пакета Ады.Text_IO.Integer_IO
procedure Put(Item : in Num; Width : in Field := Default_Width; Base : in Number_Base := Default_Base);
ставит поле с Item
выровнено по правому и белому символу в качестве наполнителя. Где Width
ширина поля и Base
10 как defualt.
пакета Ады.Веревка.Исправлено
function Head (Source : in String; Count : in Natural; Pad : in Character := Space) return String;
function Tail (Source : in String; Count : in Natural; Pad : in Character := Space) return String;
возвращает форматированную строку. Где Count
ширина поля и Pad
заполнитель для поле. Head
выравнивает строку слева. Tail
выравнивает строку направо.
пусть ширина столбца будет длиной 8 символов и используйте тире в качестве наполнителя.
Put_Line (Head ("Ashley", 8, '-'));
Put_Line (Head ("Aloha", 8, '-'));
Put_Line (Head ("Jack", 8, '-'));
Put_Line (Tail ("Ashley", 8, '-'));
Put_Line (Tail ("Aloha", 8, '-'));
Put_Line (Tail ("Jack", 8, '-'));
выход
Ashley--
Aloha---
Jack----
--Ashley
---Aloha
----Jack
атрибут discrete_type'
возвращает длину которого дискретного типа должна быть представлена в виде текста.
пример
with Ada.Text_IO; use Ada.Text_IO;
with Ada.Float_Text_IO; use Ada.Float_Text_IO;
with Ada.Integer_Text_IO; use Ada.Integer_Text_IO;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Calendar; use Ada.Calendar;
procedure Test is
subtype Index is Positive range 95 .. 1223;
procedure Put_Line ( I : in out Index; Name : String; Phone : Natural; Address : String; T : in out Time ) is
begin
Put (I, Index'Width);
Put (": ");
Put (Head (Name, 10, ' '));
Put (" | ");
Put (Tail (Phone'Img (Phone'Img'First + 1 .. Phone'Img'Last), 13, '0'));
Put (" | ");
Put (Head (Address, 20, ' '));
Put (Year (T), Year_Number'Width);
Put ("-");
Put (Month (T), Month_Number'Width);
Put ("-");
Put (Day (T), Day_Number'Width);
I := Positive'Succ (I);
T := T + Duration (60 * 60 * 24 * 3);
New_Line;
end;
I : Index := Index'First;
Now : Time := Clock;
begin
Put_Line (I, "Ashley", 1033438392, "Wellington, New Zealand", Now);
Put_Line (I, "Aloha", 01087651234, "Hawaii, United States of America", Now);
Put_Line (I, "Jack", 01082840184, "Beijing, China", Now);
I := Index'Last - 3;
Put_Line (I,"Ashley", 1033438392, "Wellington, New Zealand", Now);
Put_Line (I,"Aloha", 01087651234, "Hawaii, United States of America", Now);
Put_Line (I,"Jack", 01082840184, "Beijing, China", Now);
end;
выход
95: Ashley | 0001033438392 | Wellington, New Zeal 2015- 5- 24
96: Aloha | 0001087651234 | Hawaii, United State 2015- 5- 27
97: Jack | 0001082840184 | Beijing, China 2015- 5- 30
1220: Ashley | 0001033438392 | Wellington, New Zeal 2015- 6- 2
1221: Aloha | 0001087651234 | Hawaii, United State 2015- 6- 5
1222: Jack | 0001082840184 | Beijing, China 2015- 6- 8
I я бы рекомендовал создать тип для номера телефона, я не знаю, должен ли это быть строка или номер с нулями заголовка, номер телефона может иметь разную длину, я думаю.
вы также можете использовать GNAT.Formatted_String
пакета.
Он работает по крайней мере с Ada 2012 (не может проверить, существует ли он в Ada 2005).
Он очень похож на использование printf, но с небольшой синтаксической разницей.
вот рабочий простой пример http://tpcg.io/iJwfWa:
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Formatted_String; use GNAT.Formatted_String;
procedure Hello is
formatStr : constant String := "Hello, %-5s !";
nameArray : constant array (1..3) of String (1..4) :=
(1 => "_Foo",
2 => "_Bar",
3 => "_Fuu");
gnatFormat : Formatted_String := +(formatStr); -- initialisation needed
begin
for index in nameArray'range loop
gnatFormat := +(formatStr); --reaffectation needed
Put_Line(-(gnatFormat & nameArray(index)));
end loop;
end Hello;
выход :
(GNATMAKE v7.1.1 on https://www.tutorialspoint.com/compile_ada_online.php)
$gnatmake -o hello *.adb
gcc -c hello.adb
gnatbind -x hello.ali
gnatlink hello.ali -o hello
$hello
Hello, _Foo !
Hello, _Bar !
Hello, _Fuu !
другой пример с вашими входами http://tpcg.io/iJwfWa :
with Ada.Text_IO; use Ada.Text_IO;
with GNAT.Formatted_String; use GNAT.Formatted_String;
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
procedure Hello is
formatStr : constant String := "%-10s | %11d | %35s | %4d-%2d-%2d";
type T_element_descriptor is record
name : Unbounded_String;
number : Integer;
city : Unbounded_String;
birth_y : Integer; -- should use a date object ...
birth_m : Integer;
birth_d : Integer;
end record;
elementArray : constant array (1..3) of T_element_descriptor :=
(1 => (To_Unbounded_String("Ashley"), 01033438392, To_Unbounded_String("Wellington, New Zealand"), 1987, 4, 14),
2 => (To_Unbounded_String("Aloha"), 01087651234, To_Unbounded_String("Hawaii, United States of America"), 1988, 9, 23),
3 => (To_Unbounded_String("Jack"), 01082840184, To_Unbounded_String("Beijing, China"), 1989, 6, 19));
gnatFormat : Formatted_String := +formatStr;
begin
for index in elementArray'range loop
gnatFormat := +(formatStr);
Put_Line(-(gnatFormat
& To_String(elementArray(index).name)
& elementArray(index).number
& To_String(elementArray(index).city)
& elementArray(index).birth_y
& elementArray(index).birth_m
& elementArray(index).birth_d
));
end loop;
end Hello;
выходы:
(GNATMAKE v7.1.1 on https://www.tutorialspoint.com/compile_ada_online.php)
$gnatmake -o hello *.adb
gcc -c hello.adb
gnatbind -x hello.ali
gnatlink hello.ali -o hello
$hello
Ashley | 1033438392 | Wellington, New Zealand | 1987- 4-14
Aloha | 1087651234 | Hawaii, United States of America | 1988- 9-23
Jack | 1082840184 | Beijing, China | 1989- 6-19
лучший пример приведен в элементе g-forstr.ads
файл с gnat:
-- This package add support for formatted string as supported by C printf()
-- A simple usage is:
--
-- Put_Line (-(+"%s" & "a string"));
--
-- or with a constant for the format:
--
-- declare
-- Format : constant Formatted_String := +"%s";
-- begin
-- Put_Line (-(Format & "a string"));
-- end;
--
-- Finally a more complex example:
--
-- declare
-- F : Formatted_String := +"['%c' ; %10d]";
-- C : Character := 'v';
-- I : Integer := 98;
-- begin
-- F := F & C & I;
-- Put_Line (-F);
-- end;
-- Which will display:
-- ['v' ; 98]
-- Each format specifier is: %[flags][width][.precision][length]specifier
-- Specifiers:
-- d or i Signed decimal integer
-- u Unsigned decimal integer
-- o Unsigned octal
-- x Unsigned hexadecimal integer
-- X Unsigned hexadecimal integer (uppercase)
-- f Decimal floating point, lowercase
-- F Decimal floating point, uppercase
-- e Scientific notation (mantissa/exponent), lowercase
-- E Scientific notation (mantissa/exponent), uppercase
-- g Use the shortest representation: %e or %f
-- G Use the shortest representation: %E or %F
-- c Character
-- s String of characters
-- p Pointer address
-- % A % followed by another % character will write a single %
-- Flags:
-- - Left-justify within the given field width;
-- Right justification is the default.
-- + Forces to preceed the result with a plus or minus sign (+ or -)
-- even for positive numbers. By default, only negative numbers
-- are preceded with a - sign.
-- (space) If no sign is going to be written, a blank space is inserted
-- before the value.
-- # Used with o, x or X specifiers the value is preceeded with
-- 0, 0x or 0X respectively for values different than zero.
-- Used with a, A, e, E, f, F, g or G it forces the written
-- output to contain a decimal point even if no more digits
-- follow. By default, if no digits follow, no decimal point is
-- written.
-- ~ As above, but using Ada style based <base>#<number>#
-- 0 Left-pads the number with zeroes (0) instead of spaces when
-- padding is specified.
-- Width:
-- number Minimum number of characters to be printed. If the value to
-- be printed is shorter than this number, the result is padded
-- with blank spaces. The value is not truncated even if the
-- result is larger.
-- * The width is not specified in the format string, but as an
-- additional integer value argument preceding the argument that
-- has to be formatted.
-- Precision:
-- number For integer specifiers (d, i, o, u, x, X): precision specifies
-- the minimum number of digits to be written. If the value to be
-- written is shorter than this number, the result is padded with
-- leading zeros. The value is not truncated even if the result
-- is longer. A precision of 0 means that no character is written
-- for the value 0.
-- For e, E, f and F specifiers: this is the number of digits to
-- be printed after the decimal point (by default, this is 6).
-- For g and G specifiers: This is the maximum number of
-- significant digits to be printed.
-- For s: this is the maximum number of characters to be printed.
-- By default all characters are printed until the ending null
-- character is encountered.
-- If the period is specified without an explicit value for
-- precision, 0 is assumed.
-- .* The precision is not specified in the format string, but as an
-- additional integer value argument preceding the argument that
-- has to be formatted.