From 5a8a3749f46828f1db5cbd6bd55d22ea9e188ab1 Mon Sep 17 00:00:00 2001 From: Jed Barber Date: Sun, 5 Feb 2017 00:43:59 +1100 Subject: CSV package done, sketched out Candidates package --- src/csv.adb | 196 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/csv.adb (limited to 'src/csv.adb') diff --git a/src/csv.adb b/src/csv.adb new file mode 100644 index 0000000..eab633b --- /dev/null +++ b/src/csv.adb @@ -0,0 +1,196 @@ + + +package body CSV is + + + package SU renames Ada.Strings.Unbounded; + + + + + function Quoted + (Input : in SU.Unbounded_String; + Output : out SU.Unbounded_String; + Remaining : out SU.Unbounded_String) + return Boolean + is + Result : SU.Unbounded_String; + This_In : SU.Unbounded_String; + begin + if SU.Length (Input) > 0 and then + SU.Element (Input, 1) = Quote + then + Result := SU.To_Unbounded_String (0); + This_In := SU.Tail (Input, SU.Length (Input) - 1); + while SU.Length (This_In) > 0 loop + SU.Append (Result, SU.Head (This_In, 1)); + This_In := SU.Tail (This_In, SU.Length (This_In) - 1); + exit when SU.Length (This_In) > 0 and then SU.Element (This_In, 1) = Quote; + end loop; + Output := Result; + Remaining := SU.Tail (This_In, SU.Length (This_In) - 1); + return True; + else + return False; + end if; + end Quoted; + + + + + function Escape_Char + (Input : in SU.Unbounded_String; + Output : out SU.Unbounded_String; + Remaining : out SU.Unbounded_String) + return Boolean is + begin + if SU.Length (Input) > 1 and then + SU.Element (Input, 1) = Escape + then + Output := SU.Unbounded_Slice (Input, 2, 1); + Remaining := SU.Tail (Input, SU.Length (Input) - 2); + return True; + else + return False; + end if; + end Escape_Char; + + + + + function Field_Char + (Input : in SU.Unbounded_String; + Output : out SU.Unbounded_String; + Remaining : out SU.Unbounded_String) + return Boolean is + begin + if SU.Length (Input) > 0 and then + SU.Element (Input, 1) /= Delimiter and then + SU.Element (Input, 1) /= Quote and then + SU.Element (Input, 1) /= Escape + then + Output := SU.Head (Input, 1); + Remaining := SU.Tail (Input, SU.Length (Input) - 1); + return True; + else + return False; + end if; + end Field_Char; + + + + + procedure Field + (Input : in SU.Unbounded_String; + Output : out SU.Unbounded_String; + Remaining : out SU.Unbounded_String) + is + Result : SU.Unbounded_String; + This_In, This_Out, This_Remaining : SU.Unbounded_String; + begin + Result := SU.To_Unbounded_String (0); + This_In := Input; + while + Field_Char (This_In, This_Out, This_Remaining) or else + Escape_Char (This_In, This_Out, This_Remaining) or else + Quoted (This_In, This_Out, This_Remaining) + loop + SU.Append (Result, This_Out); + This_In := This_Remaining; + end loop; + Output := Result; + Remaining := This_In; + end Field; + + + + + function Separator + (Input : in SU.Unbounded_String; + Output : out SU.Unbounded_String; + Remaining : out SU.Unbounded_String) + return Boolean is + begin + if SU.Length (Input) > 0 and then + SU.Element (Input, 1) = Delimiter + then + Output := SU.Head (Input, 1); + Remaining := SU.Tail (Input, SU.Length (Input) - 1); + return True; + else + return False; + end if; + end Separator; + + + + + function Parse_Line + (Input : in String) + return CSV_Record + is + Result : CSV_Record := String_Vectors.Empty_Vector; + This_In, This_Out, This_Remaining : SU.Unbounded_String; + begin + This_In := SU.To_Unbounded_String (Input); + loop + Field (This_In, This_Out, This_Remaining); + Result.Append (This_Out); + This_In := This_Remaining; + exit when not Separator (This_In, This_Out, This_Remaining); + This_In := This_Remaining; + end loop; + return Result; + end Parse_Line; + + + + + function Encode_String + (Input : in SU.Unbounded_String) + return SU.Unbounded_String + is + Result : SU.Unbounded_String; + Elem : Character; + Needs_Quotes : Boolean := False; + begin + Result := SU.To_Unbounded_String (0); + for I in Integer range 1 .. SU.Length (Input) loop + Elem := SU.Element (Input, I); + if Elem = Quote or + Elem = Escape + then + SU.Append (Result, Escape & Elem); + else + SU.Append (Result, Elem); + end if; + if Elem = Delimiter then + Needs_Quotes := True; + end if; + end loop; + if Needs_Quotes then + Result := Quote & Result & Quote; + end if; + return Result; + end Encode_String; + + + + + function Unparse_Record + (Input : in CSV_Record) + return String + is + Result : SU.Unbounded_String; + begin + Result := SU.To_Unbounded_String (0); + for Item of Input loop + SU.Append (Result, Encode_String (Item) & Delimiter); + end loop; + return SU.Slice (Result, 1, SU.Length (Result) - 1); + end Unparse_Record; + + +end CSV; + + -- cgit