From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Thread: 103376,a0833bbed8752e1f X-Google-Attributes: gid103376,public X-Google-Language: ENGLISH,ASCII-7-bit Path: g2news1.google.com!news1.google.com!news.glorb.com!newsfeed00.sul.t-online.de!newsmm00.sul.t-online.de!t-online.de!news.t-online.com!not-for-mail From: Martin Krischik Newsgroups: comp.lang.ada Subject: Re: variable lenght strings Date: Fri, 22 Oct 2004 09:38:19 +0200 Organization: AdaCL Message-ID: <1240712.80bnFHAYxs@linux1.krischik.com> References: <417842cd$0$74191$39cecf19@news.twtelecom.net> Reply-To: krischik@users.sourceforge.net Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7Bit X-Trace: news.t-online.com 1098431369 03 22568 8Z3IXidLEyKWrn+ 041022 07:49:29 X-Complaints-To: usenet-abuse@t-online.de X-ID: EwHHugZHoeREoMIfSuH5UbcNg5+HQayllZbjkzu2ONTuRPAlJYSDcD User-Agent: KNode/0.8.0 Xref: g2news1.google.com comp.lang.ada:5623 Date: 2004-10-22T09:38:19+02:00 List-Id: Matthew Heaney wrote: > > "Marius Amado Alves" wrote in message > news:mailman.46.1098398641.10401.comp.lang.ada@ada-france.org... >>> 1) Is it possible to use Get_Line with Unbounded and/or Bounded >>> Strings? >> >> Not in the standard, but subprograms like those are usually around, e.g. >> in the GNAT Library, or end up being written in house. >> >>> 2) If not, how should usei input be managed when lines length isn't >>> known a priori? >> >> There's a way using the standard Get_Line, explained in AdaPower. > > Mario is probably referring to an article I posted to CLA a few years' > ago, and which is now archived at the adapower website. > > The basic idea is this: algorithms that consume input from a stream need > a > way a identify when all of the input has been consumed. Typically this is > done using a special value that you know is outside the range of normal > values, e.g. Well, you do not check for End_Of_File and that means your solution will fail if the last line is not terminated with CR/LF. And if you want to process files which have been edited by human beings than you have to care for that case The following version does work. It hast been tested on hundreds of files all edited by human beings: package body -- -- String IO Routienes. This are used because -- Ada.Strings.Unbounded.Text_IO and GNAT.IO_Aux both have a suttle -- little bug. -- AdaCL.Strings.IO is -- -- Shorten some names. -- package S_U renames Ada.Strings.Unbounded; package S_Maps renames Ada.Strings.Maps; package Latin_1 renames Ada.Characters.Latin_1; package IO renames Ada.Text_IO; -- Buffer length. Works for any non-zero value, larger values take -- more stack space, smaller values require more recursion. BufferSize : constant := 2000; -- -- Well, there are a lot of Get_Line routines around and GNAT -- certanly has its onwn, but all those I have seen have suttle bug: -- When the last line is not terminated with CR/LF and a multiple -- of buffersize long they will throw and end of file exception. -- -- This version need recursion! -- function Get_Line ( -- File to be read. File : in IO.File_Type) return String is -- Trace : AdaCL.Trace.Object := AdaCL.Trace.Function_Trace (AdaCL.Trace.Entity & ':' & AdaCL.Trace.Source); -- pragma Unreferenced (Trace); Buffer : String (1 .. BufferSize); Last : Natural; begin IO.Get_Line ( File => File, Item => Buffer, Last => Last); if Last < Buffer'Last then return Buffer (1 .. Last); elsif IO.End_Of_File (File) then return Buffer; else return Buffer & Get_Line (File); end if; end Get_Line; -- -- Well, there are a lot of Get_Line routines around and GNAT -- certanly has its onwn, but all those I have seen have suttle bug: -- When the last line is not terminated with CR/LF and a multiple -- of buffersize long they will throw and end of file exception. -- -- This version uses a loop. -- function Get_Line ( -- File to be read. File : in IO.File_Type) return S_U.Unbounded_String is -- Trace : AdaCL.Trace.Object := AdaCL.Trace.Function_Trace (AdaCL.Trace.Entity & ':' & AdaCL.Trace.Source); -- pragma Unreferenced (Trace); Retval : S_U.Unbounded_String := S_U.Null_Unbounded_String; Item : String (1 .. BufferSize); Last : Natural; begin GetWholeLine : loop IO.Get_Line ( File => File, Item => Item, Last => Last); S_U.Append ( Source => Retval, New_Item => Item (1 .. Last)); exit GetWholeLine when Last < Item'Last or IO.End_Of_File (File); end loop GetWholeLine; return Retval; end Get_Line; -- -- Get Next Word. -- procedure Get_Word ( -- File to be read. File : in Ada.Text_IO.File_Type; -- String into wich the word is to be read Item : out String; -- Actual amount of characters read. Last : out Natural; -- Word Delimiters Delimiters : in Ada.Strings.Maps.Character_Set := Word_Delimiters) is -- Trace : AdaCL.Trace.Object := AdaCL.Trace.Function_Trace (AdaCL.Trace.Entity & ':' & AdaCL.Trace.Source); -- pragma Unreferenced (Trace); Next_Char : Character := Latin_1.NUL; begin Last := Item'First; Skip_Blanks : loop IO.Get (File => File, Item => Next_Char); -- AdaCL.Trace.Write (Integer'Image (Character'Pos (Next_Char)) & "'" & String'(1 => Next_Char) & "'"); exit Skip_Blanks when not S_Maps.Is_In ( Element => Next_Char, Set => Delimiters); end loop Skip_Blanks; Read_Char : loop if S_Maps.Is_In (Element => Next_Char, Set => Delimiters) then Last := Natural'Pred (Last); exit Read_Char; end if; -- AdaCL.Trace.Write (Integer'Image (Character'Pos (Next_Char)) & "'" & String'(1 => Next_Char) & "'"); Item (Last) := Next_Char; -- AdaCL.Trace.Write (Item (Item'First .. Last)); Last := Natural'Succ (Last); exit Read_Char when Last = Item'Last; IO.Get (File => File, Item => Next_Char); end loop Read_Char; end Get_Word; -- -- Get Next Word. -- -- This version uses recursion! The actual version is garanteed to work -- up to words 2000 characters. -- function Get_Word ( -- File to be read. File : in IO.File_Type; -- Word Delimiters Delimiters : in S_Maps.Character_Set := Word_Delimiters) return String is -- Trace : AdaCL.Trace.Object := AdaCL.Trace.Function_Trace (AdaCL.Trace.Entity & ':' & AdaCL.Trace.Source); -- pragma Unreferenced (Trace); Buffer : String (1 .. BufferSize); Last : Natural; begin Get_Word (File => File, Item => Buffer, Last => Last, Delimiters => Delimiters); if Last < Buffer'Last then return Buffer (1 .. Last); elsif IO.End_Of_File (File) then return Buffer; else return Buffer & Get_Word (File, Delimiters); end if; end Get_Word; -- -- Get Next Word. -- -- This version uses a loop. The actual version is garanteed to work -- up to words 2000 characters. -- function Get_Word ( -- File to be read. File : in IO.File_Type; -- Word Delimiters Delimiters : in Ada.Strings.Maps.Character_Set := Word_Delimiters) return S_U.Unbounded_String is -- Trace : AdaCL.Trace.Object := AdaCL.Trace.Function_Trace (AdaCL.Trace.Entity & ':' & AdaCL.Trace.Source); -- pragma Unreferenced (Trace); Retval : S_U.Unbounded_String := S_U.Null_Unbounded_String; Item : String (1 .. BufferSize); Last : Natural; begin GetWholeLine : loop Get_Word (File => File, Item => Item, Last => Last, Delimiters => Delimiters); S_U.Append (Source => Retval, New_Item => Item (1 .. Last)); exit GetWholeLine when Last < Item'Last or IO.End_Of_File (File); end loop GetWholeLine; return Retval; end Get_Word; end AdaCL.Strings.IO; With Regards Martin -- mailto://krischik@users.sourceforge.net http://www.ada.krischik.com