有没有办法在Ada中格式化文本输出

时间:2011-05-17 14:19:26

标签: string text format 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-%2d\n",name,phone,address,year,month,day);

在Ada 05中可以进行这种格式化吗?

PS请忽略姓名,电话号码,地址和生日。我在30秒内完成了它们......

6 个答案:

答案 0 :(得分:8)

可以这样做,但机制有点麻烦,而且相当冗长 我通常会做的是编写单独的程序来处理更复杂的输出,例如:为清晰起见,将日期与其余的字符串处理结合使用。

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的输出操作,但我通常不是默认截断的粉丝,因为允许字符串超出请求的宽度或下一行的缩进倾向于make格式错误更明显。

因此,根据上面的代码,打印出类似第一行的内容可能类似于:

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);

Ada中的文本IO通常很麻烦,但通常具有使您正在做的事情相对清晰的优点。

答案 1 :(得分:4)

请注意,在C ++中,这些天printf()即将被折旧,转而使用带有流格式化程序的流。这是方便的,但是大规模不安全(至少在几个意义上)。现在,鼓励开发人员使用C ++流(使用各种操纵器)。

在Ada中,您可以使用字符串catenation运算符&以与C ++流非常相似的方式处理字符串,其中C ++伙伴使用流插入运算符(<<)。在某些方面,Ada的方法更好,因为您可以嵌套链接表达式,而不能使用插入流的表达式。

这里的问题是,setfill()hexsetw()等C ++格式化程序没有任何便利的等价物。真的应该是,并且(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()界面,那么{A}当然可以从Ada调用printf()。你必须担心在Ada的大小字符串和C的nul终止字符串之间转换,但这就是Ada.Interfaces.C.Strings的用途。

答案 2 :(得分:3)

是的。虽然它不像c中那么容易。

查看§A.4.4 Bounded-Length String Handling有关如何创建预定义大小的字符串,并使用integer'image转换数字。 &amp;运算符可用于使用ada.text_io.put_line()连接字符串和输出。

答案 3 :(得分:2)

您可能会喜欢这个简单的card game simulation使用Ada.Strings.Fixed格式化ASCII图表的范围轴标签。请参阅function Label,该Tail使用TrimInteger'Image格式化LowerUpper值的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}}

答案 4 :(得分:2)

此特定格式设置有辅助工具。

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为defualt。

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

我建议为电话号码创建一个类型,我不知道如果它应该是字符串或带有标题为零的数字,我猜的电话号码可以有不同的长度。

答案 5 :(得分:1)

您还可以使用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

gnat提供的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.