Есть ли способ отформатировать вывод текста в Аде - PullRequest
7 голосов
/ 17 мая 2011

Есть ли способ отформатировать выводимую строку?Я пытаюсь получить красивый вид следующего вывода

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-%2d\n",name,phone,address,year,month,day);

Было бы возможно сделать этоформатирования в Аде 05?

PS Пожалуйста, просто игнорируйте имена, номера телефонов, адрес и дату рождения.Я сделал их за 30 секунд ...

Ответы [ 6 ]

8 голосов
/ 17 мая 2011

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

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;

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

Я добавил туда усечение строки как шанс использовать нарезку массива и манипулирование выводом 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);

Текстовый ввод-вывод в Аде обычно громоздок,но, как правило, обладает достаточной ясностью относительно того, что вы делаете.

4 голосов
/ 17 мая 2011

Обратите внимание, что в C ++ в наши дни printf() находится на грани амортизации в пользу использования потоков с потоковыми форматерами.Это удобно, но крайне небезопасно (по крайней мере, в нескольких смыслах этого слова).В наши дни разработчикам рекомендуется вместо этого использовать потоки C ++ (с их различными манипуляторами).

В Ada вы можете манипулировать строками в стиле, очень похожем на потоки C ++, используя оператор объединения строк &, где люди C ++ используютоператор вставки потока (<<).В некотором смысле, метод Ады лучше, потому что вы можете вкладывать вложенные выражения, чего нельзя сделать с помощью выражений, вставленных в поток.

Проблема здесь в том, что нет никаких удобных эквивалентов форматерам C ++, таким как setfill(), hex и setw().Они действительно должны быть, и (hex исключено) их не сложно написать самостоятельно, но пока их нет.

Например, 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() вполне может вызываться из Ады.Вам нужно беспокоиться о переходе между строками размера Ады и строками C с нулевым символом в конце, но для этого есть Ada.Interfaces.C.Strings.

3 голосов
/ 17 мая 2011

Да, есть.Хотя это не так просто, как в c.

Взгляните на §A.4.4 Обработка строк с ограниченной длиной , чтобы узнать, как создавать строки предопределенного размера, и использовать integer'image дляконвертируй свои числа.Оператор & полезен для объединения строк и вывода с использованием ada.text_io.put_line ().

2 голосов
/ 24 мая 2015

Для этой конкретной настройки формата есть вспомогательные инструменты.

Пакет Ada.Text_IO.Integer_IO

procedure Put(Item : in Num; Width : in Field := Default_Width; Base : in Number_Base := Default_Base);

Помещает поле с Item, выровненным по правому краюи пробел в качестве наполнителя.Где Width - ширина поля, а Base - 10 по умолчанию.

Пакет Ada.Strings.Fixed

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 'Width

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

Пример

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

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

2 голосов
/ 17 мая 2011

Вам может понравиться этот простой симулятор карточной игры , который использует Ada.Strings.Fixed для форматирования меток оси диапазона для графика ASCII.См. function Label, который использует Tail и Trim для форматирования Integer'Image значения 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;

Консоль:

Distribution of lengths:
0000-0100 |**********
0100-0200 |*****************************
0200-0300 |**********************
0300-0400 |***************
0400-0500 |**********
0500-0600 |*******
0600-0700 |****
0700-0800 |****
0800-0900 |**
0900-1000 |**
1000-1100 |*
1100-1200 |*
1 голос
/ 18 июня 2018

Вы также можете использовать пакет 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с комарами:

--  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.
...