CSV to HTML translation: Difference between revisions

From Rosetta Code
Content added Content deleted
(Added Ruby version.)
Line 1,104: Line 1,104:
===HTML outputs rendered in firefox browser===
===HTML outputs rendered in firefox browser===
[[File:Csv2html.PNG|frame||none]]
[[File:Csv2html.PNG|frame||none]]

=={{header|Ruby}}==
The extra credit version has one extra line compared to the non-extra credit version. To output a header, simply add "header" to the command line:
ruby csv2html.rb header
I/O is done through standard input/output.
<lang ruby>require 'cgi'

puts '<table summary="csv2html program output">'

def row2html str, wrap = "td"
"<tr>" +
str.split(",").map { |cell| "<#{wrap}>#{CGI.escapeHTML cell}</#{wrap}>" }.join +
"</tr>"
end

puts row2html gets.chomp, "th" if ARGV.delete "header"

while str = gets
puts row2html str.chomp
end

puts "</table>"</lang>Sample output:<lang html><table summary="csv2html program output">
<tr><th>Character</th><th>Speech</th></tr>
<tr><td>The multitude</td><td>The messiah! Show us the messiah!</td></tr>
<tr><td>Brians mother</td><td>&lt;angry&gt;Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!&lt;/angry&gt;</td></tr>
<tr><td>The multitude</td><td>Who are you?</td></tr>
<tr><td>Brians mother</td><td>I'm his mother; that's who!</td></tr>
<tr><td>The multitude</td><td>Behold his mother! Behold his mother!</td></tr>
</table></lang>


=={{header|Tcl}}==
=={{header|Tcl}}==

Revision as of 05:08, 24 April 2011

Task
CSV to HTML translation
You are encouraged to solve this task according to the task description, using any language you may know.

Consider a simplified CSV format where all rows are separated by a newline and all columns are separated by commas. No commas are allowed as field data, but the data may contain other characters and character sequences that would normally be escaped when converted to HTML

The task is to create a function that takes a string representation of the CSV data and returns a text string of an HTML table representing the CSV data. Use the following data as the CSV text to convert, and show your output.

Character,Speech
The multitude,The messiah! Show us the messiah!
Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitude,Who are you?
Brians mother,I'm his mother; that's who!
The multitude,Behold his mother! Behold his mother!

For extra credit, optionally allow special formatting for the first row of the table as if it is the tables header row.

Ada

Works with: Ada 2005
Library: AWS

csv2html.adb: <lang Ada>with Ada.Strings.Fixed; with Ada.Text_IO; with Templates_Parser;

procedure Csv2Html is

  use type Templates_Parser.Vector_Tag;
  Chars : Templates_Parser.Vector_Tag;
  Speeches : Templates_Parser.Vector_Tag;
  CSV_File : Ada.Text_IO.File_Type;

begin

  -- read the csv data
  Ada.Text_IO.Open (File => CSV_File,
                    Mode => Ada.Text_IO.In_File,
                    Name => "data.csv");
  -- fill the tags
  while not Ada.Text_IO.End_Of_File (CSV_File) loop
     declare
        Whole_Line : String := Ada.Text_IO.Get_Line (CSV_File);
        Comma_Pos : Natural := Ada.Strings.Fixed.Index (Whole_Line, ",");
     begin
        Chars := Chars & Whole_Line (Whole_Line'First .. Comma_Pos - 1);
        Speeches := Speeches & Whole_Line (Comma_Pos + 1 .. Whole_Line'Last);
     end;
  end loop;
  Ada.Text_IO.Close (CSV_File);
  -- build translation table and output html
  declare
     Translations : constant Templates_Parser.Translate_Table :=
       (1 => Templates_Parser.Assoc ("CHAR", Chars),
        2 => Templates_Parser.Assoc ("SPEECH", Speeches));
  begin
     Ada.Text_IO.Put_Line
       (Templates_Parser.Parse ("table.tmplt", Translations));
  end;

end Csv2Html;</lang>

table.tmplt:

<table>
@@TABLE@@
   <tr>
      <td>@_WEB_ESCAPE:CHAR_@</td>
      <td>@_WEB_ESCAPE:SPEECH_@</td>
   </tr>
@@END_TABLE@@
</table>

Output:

<table>
   <tr>
      <td>Character</td>
      <td>Speech</td>
   </tr>
   <tr>
      <td>The multitude</td>
      <td>The messiah! Show us the messiah!</td>
   </tr>
   <tr>
      <td>Brians mother</td>
      <td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td>
   </tr>
   <tr>
      <td>The multitude</td>
      <td>Who are you?</td>
   </tr>
   <tr>
      <td>Brians mother</td>
      <td>I'm his mother; that's who!</td>
   </tr>
   <tr>
      <td>The multitude</td>
      <td>Behold his mother! Behold his mother!</td>
   </tr>
</table>

C

This example is incomplete. Output not shown. Please ensure that it meets all task requirements and remove this message.

This produces a full bare html (tidy gives 1 warning) with styles embedded but not "inlined"; it does not escape characters that does not need to be escaped (provided that the correct encoding matching the encoding of the input is given; currently hard-endoded UTF-8). <lang c>#include <stdio.h>

  1. include <stddef.h>
  2. include <stdlib.h>
  3. include <string.h>
  1. define BUF_LEN 12

void html_min_header(const char *enc, const char *title) {

 printf("<html><head><title>%s</title>"

"<meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">" "<style type=\"text/css\">" "</style></head><body>", title, enc); }

void html_min_footer(void) {

 printf("</body></html>");

}

void escape_html(char *o, int c) {

 static const char *specials = "<>&";
 static const char *map[] = { "<", ">", "&"};
 ptrdiff_t pos;
 char *p;
 
 if ( (p = strchr(specials, c)) != NULL ) {
   pos = p - specials;
   if (o != NULL) strcpy(o, map[pos]);
 } else {
   o[0] = c;
   o[1] = '\0';
 }

}

void add_column(const char *type, int c) {

 char buf[BUF_LEN];
 if ( c == '\n' ) return;
 printf("<%s>", type);
 for(; c != EOF && c != '\n'; c = getchar()) {
   if (c == ',') {
     printf("</%s><%s>", type, type);
     continue;
   }
   escape_html(buf, c);
   printf("%s", buf);
 }
 printf("</%s>", type);

}

enum mode {

 FIRST = 1, NEXT

}; int main(int argc, char **argv) {

 int c;
 enum mode status = FIRST;
 html_min_header("utf-8", "CSV converted into HTML");

printf("

"); while( (c = getchar()) != EOF ) { printf(""); switch(status) { case FIRST: add_column("th", c); status = NEXT; break; case NEXT: default: add_column("td", c); break; } printf(""); } printf("

");

 html_min_footer();
 return EXIT_SUCCESS;

}</lang>

Groovy

Solution #1: Nested GStrings

Brute force solution using nested GStrings. It solves both the basic and extra credit tasks. <lang groovy>def formatCell = { cell ->

"${cell.replaceAll('&','&').replaceAll('<','<')}"

}

def formatRow = { row ->

"""${row.split(',').collect { cell -> formatCell(cell) }.join()} """ } def formatTable = { csv, header=false -> def rows = csv.split('\n').collect { row -> formatRow(row) } header \  ? """

<thead> ${rows[0]}</thead> <tbody> ${rows[1..-1].join()}</tbody>

""" \

       : """
${rows.join()}

""" }

def formatPage = { title, csv, header=false -> """<html> <head> <title>${title}</title> <style type="text/css"> td {background-color:#ddddff; } thead td {background-color:#ddffdd; text-align:center; } </style> </head> <body>${formatTable(csv, header)}</body> </html>""" }</lang>

Test: <lang groovy>def csv = Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!

println 'Basic:' println '-----------------------------------------' println (formatPage('Basic', csv)) println '-----------------------------------------' println() println() println 'Extra Credit:' println '-----------------------------------------' println (formatPage('Extra Credit', csv, true)) println '-----------------------------------------'</lang>

Basic output:

<html>
<head>
<title>Basic</title>
<style type="text/css">
td {background-color:#ddddff; }
thead td {background-color:#ddffdd; text-align:center; }
</style>
</head>
<body>
<table>
<tr><td>Character</td><td>Speech</td></tr>
<tr><td>The multitude</td><td>The messiah! Show us the messiah!</td></tr>
<tr><td>Brians mother</td><td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td></tr>
<tr><td>The multitude</td><td>Who are you?</td></tr>
<tr><td>Brians mother</td><td>I'm his mother; that's who!</td></tr>
<tr><td>The multitude</td><td>Behold his mother! Behold his mother!</td></tr>
</table>
</body>
</html>


Appearance as rendered in Google Chrome.

Extra Credit output:

<html>
<head>
<title>Extra Credit</title>
<style type="text/css">
td {background-color:#ddddff; }
thead td {background-color:#ddffdd; text-align:center; }
</style>
</head>
<body>
<table>
<thead>
<tr><td>Character</td><td>Speech</td></tr>
</thead>
<tbody>
<tr><td>The multitude</td><td>The messiah! Show us the messiah!</td></tr>
<tr><td>Brians mother</td><td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td></tr>
<tr><td>The multitude</td><td>Who are you?</td></tr>
<tr><td>Brians mother</td><td>I'm his mother; that's who!</td></tr>
<tr><td>The multitude</td><td>Behold his mother! Behold his mother!</td></tr>
</tbody>
</table>
</body>
</html>


Appearance as rendered in Google Chrome.

Solution #2: MarkupBuilder

A much cleaner solution using the Groovy XML MarkupBuilder class. It solves both the basic and extra credit tasks. <lang groovy>import groovy.xml.MarkupBuilder

def formatRow = { doc, row ->

   doc.tr { row.each { cell -> td { mkp.yield(cell) } } }

}

def formatPage = { titleString, csv, header=false ->

   def writer = new StringWriter()
   def doc = new MarkupBuilder(writer)
   def rows = csv.split('\n').collect { row -> row.split(',') }
   doc.html {
       head {
           title (titleString)
           style (type:"text/css") { 
               mkp.yield(
                   td {background-color:#ddddff; }
                   thead td {background-color:#ddffdd; text-align:center; }
               )
           }
       }
       body {
           table {
               header && thead { formatRow(doc, rows[0]) }
               header && tbody { rows[1..-1].each { formatRow(doc, it) } }
               header || rows.each { formatRow(doc, it) }
           }
       }
   }
   writer.toString()

}</lang>

Test:
The interface is the same for both solutions, so we just reuse the same test as before.

Basic output:

<html>
  <head>
    <title>Basic</title>
    <style type='text/css'>
                    td {background-color:#ddddff; }
                    thead td {background-color:#ddffdd; text-align:center; }
                </style>
  </head>
  <body>
    <table>
      <tr>
        <td>Character</td>
        <td>Speech</td>
      </tr>
      <tr>
        <td>The multitude</td>
        <td>The messiah! Show us the messiah!</td>
      </tr>
      <tr>
        <td>Brians mother</td>
        <td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td>
      </tr>
      <tr>
        <td>The multitude</td>
        <td>Who are you?</td>
      </tr>
      <tr>
        <td>Brians mother</td>
        <td>I'm his mother; that's who!</td>
      </tr>
      <tr>
        <td>The multitude</td>
        <td>Behold his mother! Behold his mother!</td>
      </tr>
    </table>
  </body>
</html>

The HTML for this solution looks superficially different than that from the GString solution, but the appearance as rendered in Google Chrome is identical.

Extra Credit output:

<html>
  <head>
    <title>Extra Credit</title>
    <style type='text/css'>
                    td {background-color:#ddddff; }
                    thead td {background-color:#ddffdd; text-align:center; }
                </style>
  </head>
  <body>
    <table>
      <thead>
        <tr>
          <td>Character</td>
          <td>Speech</td>
        </tr>
      </thead>
      <tbody>
        <tr>
          <td>The multitude</td>
          <td>The messiah! Show us the messiah!</td>
        </tr>
        <tr>
          <td>Brians mother</td>
          <td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td>
        </tr>
        <tr>
          <td>The multitude</td>
          <td>Who are you?</td>
        </tr>
        <tr>
          <td>Brians mother</td>
          <td>I'm his mother; that's who!</td>
        </tr>
        <tr>
          <td>The multitude</td>
          <td>Behold his mother! Behold his mother!</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>

The HTML for this solution looks superficially different than that from the GString solution, but the appearance as rendered in Google Chrome is identical.

Icon and Unicon

This solution for the extra credit works in both Icon and Unicon. The simple CSV is read from standard input and written to standard output. The presence/abscend of "-heading" in the argument list sets the variable thead to the procedure writes or a 1 (for more on this see Introduction to Icon/Unicon - Conjunction yielding different results).

<lang Icon>procedure main(arglist) local pchar,row,thead

pchar := &letters ++ &digits ++ '!?;. ' # printable chars

write("

") thead := if !arglist == "-heading" then writes else 1 while row := trim(read()) do { thead("<THEAD>") writes("") thead("</THEAD>") thead := 1 } every write("</TBODY>") end</lang> Output: <lang html>
")
  while *row > 0 do
row ?:= ( (=",",writes("
")) | writes( tab(many(pchar)) | ("&#" || ord(move(1))) ), tab(0)) write("
<THEAD> </THEAD>

</TBODY></lang>

J

Solution (extra credit) <lang j>require 'strings tables/csv' encodeHTML=: ('&';'&';'<';'<';'>';'>')&stringreplace

tag=: adverb define

 'starttag endtag'=.m
 (,&.>/)"1 (starttag , ,&endtag) L:0 y

)

markupCells=: ('') tag markupHdrCells=: ('') tag markupRows=: ('';'',LF) tag markupTable=: (('
CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother&#60angry&#62Now you listen here! He&#39s not the messiah; he&#39s a very naughty boy! Now go away!&#60&#47angry&#62
The multitudeWho are you?
Brians motherI&#39m his mother; that&#39s who!
The multitudeBehold his mother! Behold his mother!
';'';'
',LF);'

') tag

makeHTMLtablefromCSV=: verb define

 0 makeHTMLtablefromCSV y             NB. default left arg is 0 (no header row)
 t=. fixcsv encodeHTML y
 if. x do. t=. (markupHdrCells@{. , markupCells@}.) t
     else. t=. markupCells t
 end.
 ;markupTable markupRows t

)</lang>

For those interested, equivalent tacit versions of tag and makeHTMLtablefromCSV are: <lang j>tag=: adverb def '[: (,&.>/)"1 m&(0&{::@[ , 1&{::@[ ,~ ]) L:0@]' makeHTMLtablefromCSV6=: 0&$: : ([: ; markupTable@markupRows@([ markupCells`(markupHdrCells@{. , markupCells@}.)@.[ fixcsv@encodeHTML))</lang>

Example <lang j> CSVstrng=: noun define Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother! )

  1 makeHTMLtablefromCSV CSVstrng</lang>

HTML output:

<lang html>

CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</lang>

OCaml

<lang ocaml>let csv_data = "\ Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; \

             he's a very naughty boy! Now go away!</angry>

The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!"

(* some utility functions *)

let string_of_char = String.make 1 ;;

let string_of_string_list = String.concat ""

let char_list_of_string str =

 let lst = ref [] in
 String.iter (fun c -> lst := c :: !lst) str;
 (List.rev !lst)

(** escape chars that need to be escaped *) let escape str =

 let chars = char_list_of_string str in
 let rec aux acc = function
 | [] -> (List.rev acc)
 | c :: tl ->
     match c with
     | 'A'..'Z'
     | 'a'..'z'
     | '0'..'9'
     | ' ' | ';' | '!' | '?' ->
         aux ((string_of_char c)::acc) tl
     | c ->
         let esc_char = (Printf.sprintf "&#%04d;" (Char.code c)) in
         aux (esc_char::acc) tl
 in
 string_of_string_list (aux [] chars)

(* now the main part *)

let extract_csv_data ~csv_data:s =

 let len = String.length s in
 let rec aux acc_line acc i j =
   if i = len
   then
     let sub = String.sub s j (i - j) in
     List.rev ((acc_line @ [escape sub])::acc)
   else
     match csv_data.[i] with
     | ',' ->
         let sub = String.sub s (j+1) (i - j - 1) in
         aux ((escape sub)::acc_line) acc (succ i) (succ i)
     | '\n' ->
         let sub = String.sub s j (i - j) in
         let acc_line = List.rev (escape sub::acc_line) in
         aux [] (acc_line::acc) (succ i) i
     | _ ->
         aux acc_line acc (succ i) j
 in
 aux [] [] 0 (-1)

let print_html_table segments =

print_string "

\n"; List.iter (fun line -> print_string "\n"; List.iter (Printf.printf " ") line; print_string "\n\n"; ) segments; print_string "
%s

\n";

let () =

 let segments = extract_csv_data ~csv_data in
 print_html_table segments</lang>

Sample html output:

<lang html>

Character Speech
The multitude The messiah! Show us the messiah!
Brians mother <angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitude Who are you?
Brians mother I'm his mother; that's who!
The multitude Behold his mother! Behold his mother!

</lang>

Extra credit version: <lang ocaml>let csv_data = "\ Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; \

             he's a very naughty boy! Now go away!</angry>

The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!"

(* some utility functions *)

let string_of_char = String.make 1 ;;

let string_of_string_list = String.concat ""

let char_list_of_string str =

 let lst = ref [] in
 String.iter (fun c -> lst := c :: !lst) str;
 (List.rev !lst)

(** escape chars that need to be escaped *) let escape str =

 let chars = char_list_of_string str in
 let rec aux acc = function
 | [] -> (List.rev acc)
 | c :: tl ->
     match c with
     | 'A'..'Z'
     | 'a'..'z'
     | '0'..'9'
     | ' ' | ';' | '!' | '?' ->
         aux ((string_of_char c)::acc) tl
     | c ->
         let esc_char = (Printf.sprintf "&#%04d;" (Char.code c)) in
         aux (esc_char::acc) tl
 in
 string_of_string_list (aux [] chars)

(* now the main part *)

let extract_csv_data ~csv_data:s =

 let len = String.length s in
 let rec aux acc_line acc i j =
   if i = len
   then
     let sub = String.sub s j (i - j) in
     List.rev ((acc_line @ [escape sub])::acc)
   else
     match csv_data.[i] with
     | ',' ->
         let sub = String.sub s (j+1) (i - j - 1) in
         aux (escape sub::acc_line) acc (succ i) (succ i)
     | '\n' ->
         let sub = String.sub s j (i - j) in
         let acc_line = List.rev (escape sub::acc_line) in
         aux [] (acc_line::acc) (succ i) i
     | _ ->
         aux acc_line acc (succ i) j
 in
 aux [] [] 0 (-1)


let style_th = "style='color:#000; background:#FF0;'" let style_td = "style='color:#000; background:#8FF; \

                      border:1px #000 solid; padding:0.6em;'"

let print_html_table segments =

print_string "

\n"; let print_line tag_open tag_close lines = List.iter (fun line -> Printf.printf " %s%s%s" tag_open line tag_close; ) lines in begin match segments with | head :: body -> print_string " <thead>\n"; print_string " \n"; print_line (" \n" head; print_string " \n"; print_string " </thead>\n"; (); print_string " <tbody>\n"; List.iter (fun line -> print_string " \n"; List.iter (Printf.printf " \n" style_td) line; print_string " \n"; ) body; print_string " </tbody>\n"; | _ -> () end; print_string "
") "
%s

\n";

let () =

 let segments = extract_csv_data ~csv_data in
 print_html_table segments</lang>

Output:

<lang html>

<thead> </thead> <tbody> </tbody>
Character Speech
The multitude The messiah! Show us the messiah!
Brians mother <angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitude Who are you?
Brians mother I'm his mother; that's who!
The multitude Behold his mother! Behold his mother!

</lang>

Perl

Provide the CSV data as standard input. With a command-line argument, the first row will use instead of .

<lang perl>use HTML::Entities;

sub row {

   my $elem = shift;
   my @cells = map {"<$elem>$_</$elem>"} split ',', shift;

print '', @cells, "\n"; } my ($first, @rest) = map {my $x = $_; chomp $x; encode_entities $x} <STDIN>; print "

\n"; row @ARGV ? 'th' : 'td', $first; row 'td', $_ foreach @rest; print "

\n";</lang>

Output (with a command-line argument):

<lang html4strict>

CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</lang>

PicoLisp

Simple solution

<lang PicoLisp>(load "@lib/http.l")

(in "text.csv"

(

'myStyle NIL NIL (prinl) (while (split (line) ",") (<row> NIL (ht:Prin (pack (car @))) (ht:Prin (pack (cadr @)))) (prinl) ) ) )</lang> Output:
<table class="myStyle">
<tr><td>Character</td><td>Speech</td></tr>
<tr><td>The multitude</td><td>The messiah! Show us the messiah!</td></tr>
<tr><td>Brians mother</td><td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td></tr>
<tr><td>The multitude</td><td>Who are you?</td></tr>
<tr><td>Brians mother</td><td>I'm his mother; that's who!</td></tr>
<tr><td>The multitude</td><td>Behold his mother! Behold his mother!</td></tr>
</table>

Extra credit solution

<lang PicoLisp>(load "@lib/http.l")

(in "text.csv"

  (when (split (line) ",")
(
'myStyle NIL (mapcar '((S) (list NIL (pack S))) @) (prinl) (while (split (line) ",") (<row> NIL (ht:Prin (pack (car @))) (ht:Prin (pack (cadr @)))) (prinl) ) ) ) )</lang> Output:
<table class="myStyle"><tr><th>Character</th><th>Speech</th></tr>
<tr><td>The multitude</td><td>The messiah! Show us the messiah!</td></tr>
<tr><td>Brians mother</td><td><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry></td></tr>
<tr><td>The multitude</td><td>Who are you?</td></tr>
<tr><td>Brians mother</td><td>I'm his mother; that's who!</td></tr>
<tr><td>The multitude</td><td>Behold his mother! Behold his mother!</td></tr>
</table>

Prolog

Uses DCG. Works with SWI-Prolog.

Simple solution

<lang Prolog>csv_html :- L = "Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!",

csv_html(L, Out, []), string_to_list(Str, Out), writeln(Str).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % simple HTML % csv_html(L) -->

"
\n", csv_tr(L), "

".

csv_tr([]) --> [].

csv_tr(L) -->

"\n", csv_td(L, S), "\n\n", csv_tr(S). csv_td(L, S) --> "",

csv_td_in(L, S),

"". csv_td_in([], []) --> []. csv_td_in([10|L], L) --> []. csv_td_in([44|L], S) --> "",

csv_td_in(L,S).

csv_td_in([60|T], S) --> "<", csv_td_in(T, S).

csv_td_in([62|T], S) --> ">", csv_td_in(T, S).

csv_td_in([H|T], S) --> [H], csv_td_in(T, S).

</lang> OutPut :

<TABLE>
<TR>
<TD>Character</TD><TD>Speech</TD>
</TR>
<TR>
<TD>The multitude</TD><TD>The messiah! Show us the messiah!</TD>
</TR>
<TR>
<TD>Brians mother</TD><TD><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!></angry></TD>
</TR>
<TR>
<TD>The multitude</TD><TD>Who are you?</TD>
</TR>
<TR>
<TD>Brians mother</TD><TD>I'm his mother; that's who!</TD>
</TR>
<TR>
<TD>The multitude</TD><TD>Behold his mother! Behold his mother!</TD>
</TR>
</TABLE>

Extra credit solution

<lang Prolog>csv_html_plus :- L = "Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!",

csv_html_plus(L, Out1, []), string_to_list(Str1, Out1), writeln(Str1).

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % HTML + % csv_html_plus(L) -->

"

\n", csv_head(L, R), csv_body(R), "

".


csv_head(L, R) --> "<THEAD>\n", csv_head_tr(L, R), "</THEAD>\n".


csv_head_tr(L, R) -->

"\n", csv_head_th(L, R), "\n\n". csv_head_th(L, S) --> "",

csv_head_th_in(L, S),

"". csv_head_th_in([], []) --> []. csv_head_th_in([10|L], L) --> []. csv_head_th_in([44|L], S) --> "",

csv_head_th_in(L,S).

csv_head_th_in([H|T], S) --> [H], csv_head_th_in(T, S).


csv_body(L) --> "<TBODY>\n", csv_body_tr(L), "</TBODY>\n".

csv_body_tr([]) --> [].

csv_body_tr(L) -->

"\n", csv_body_td(L, S), "\n\n", csv_body_tr(S). csv_body_td(L, S) --> "",

csv_body_td_in(L, S),

"". csv_body_td_in([], []) --> []. csv_body_td_in([10|L], L) --> []. csv_body_td_in([44|L], S) --> "",

csv_body_td_in(L,S).

csv_body_td_in([60|T], S) --> "<", csv_body_td_in(T, S).

csv_body_td_in([62|T], S) --> ">", csv_body_td_in(T, S).

csv_body_td_in([H|T], S) --> [H], csv_body_td_in(T, S). </lang> Output :

<TABLE>
<THEAD>
<TR>
<TH style='color:#000; background:#FF0;'>Character</TH><TH style='color:#000; background:#FF0;'>Speech</TH>
</TR>
</THEAD>
<TBODY>
<TR>
<TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>The multitude</TD><TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>The messiah! Show us the messiah!</TD>
</TR>
<TR>
<TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>Brians mother</TD><TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'><angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!></angry></TD>
</TR>
<TR>
<TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>The multitude</TD><TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>Who are you?</TD>
</TR>
<TR>
<TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>Brians mother</TD><TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>I'm his mother; that's who!</TD>
</TR>
<TR>
<TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>The multitude</TD><TD  style='color:#000; background:#8FF; border:1px #000 solid; padding:0.6em;'>Behold his mother! Behold his mother!</TD>
</TR>
</TBODY>
</TABLE>

HTML outputs rendered in firefox browser

Python

(Note: rendered versions of both outputs are shown at the foot of this section).

Simple solution

<lang python>csvtxt = \ Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!\

from cgi import escape

def _row2tr(row, attr=None):

   cols = escape(row).split(',')

return ('' + .join('%s' % data for data in cols) + '') def csv2html(txt): htmltxt = '

\n' for rownum, row in enumerate(txt.split('\n')): htmlrow = _row2tr(row) htmlrow = ' <TBODY>%s</TBODY>\n' % htmlrow htmltxt += htmlrow htmltxt += '

\n'

   return htmltxt

htmltxt = csv2html(csvtxt) print(htmltxt)</lang>

Sample HTML output

<lang html>

<TBODY></TBODY> <TBODY></TBODY> <TBODY></TBODY> <TBODY></TBODY> <TBODY></TBODY> <TBODY></TBODY>
CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</lang>

Extra credit solution

<lang python>def _row2trextra(row, attr=None):

   cols = escape(row).split(',')
   attr_tr = attr.get('TR', )
   attr_td = attr.get('TD', )
   return (('<TR%s>' % attr_tr)

+ .join('<TD%s>%s' % (attr_td, data) for data in cols) + '') def csv2htmlextra(txt, header=True, attr=None): ' attr is a dictionary mapping tags to attributes to add to that tag' attr_table = attr.get('TABLE', ) attr_thead = attr.get('THEAD', ) attr_tbody = attr.get('TBODY', ) htmltxt = '<TABLE%s>\n' % attr_table for rownum, row in enumerate(txt.split('\n')): htmlrow = _row2trextra(row, attr) rowclass = ('THEAD%s' % attr_thead) if (header and rownum == 0) else ('TBODY%s' % attr_tbody) htmlrow = ' <%s>%s</%s>\n' % (rowclass, htmlrow, rowclass[:5]) htmltxt += htmlrow htmltxt += '\n'

   return htmltxt

htmltxt = csv2htmlextra(csvtxt, True,

                       dict(TABLE=' border="1" summary="csv2html extra program output"',
                            THEAD=' bgcolor="yellow"',
                            TBODY=' bgcolor="orange"' 
                            )
                       )

print(htmltxt)</lang>

Sample HTML output

<lang html>

<THEAD bgcolor="yellow"></THEAD> <TBODY bgcolor="orange"></TBODY> <TBODY bgcolor="orange"></TBODY> <TBODY bgcolor="orange"></TBODY> <TBODY bgcolor="orange"></TBODY> <TBODY bgcolor="orange"></TBODY>
CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</lang>

HTML outputs rendered in firefox browser

Ruby

The extra credit version has one extra line compared to the non-extra credit version. To output a header, simply add "header" to the command line:

 ruby csv2html.rb header

I/O is done through standard input/output. <lang ruby>require 'cgi'

puts '

' def row2html str, wrap = "td" "" + str.split(",").map { |cell| "<#{wrap}>#{CGI.escapeHTML cell}</#{wrap}>" }.join + "" end puts row2html gets.chomp, "th" if ARGV.delete "header" while str = gets puts row2html str.chomp end puts "

"</lang>Sample output:<lang html>

CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</lang>

Tcl

Library: Tcllib (Package: csv)
Library: Tcllib (Package: html)
Library: Tcllib (Package: struct::queue)

<lang tcl>package require Tcl 8.5 package require csv package require html package require struct::queue

set csvData "Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!"

struct::queue rows foreach line [split $csvData "\n"] {

   csv::split2queue rows $line

} html::init puts [subst {

   [html::openTag table {summary="csv2html program output"}]
   [html::while {[rows size]} {

[html::row {*}[html::quoteFormValue [rows get]]]

   }]
   [html::closeTag]

}]</lang>

Extra credit version: <lang tcl>package require Tcl 8.5 package require csv package require html package require struct::queue

set csvData "Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother!"

html::init {

   table.border 1
   table.summary "csv2html program output"
   tr.bgcolor orange

}

  1. Helpers; the html package is a little primitive otherwise

proc table {contents {opts ""}} {

   set out [html::openTag table $opts]
   append out [uplevel 1 [list subst $contents]]
   append out [html::closeTag]

} proc tr {list {ropt ""}} {

   set out [html::openTag tr $ropt]
   foreach x $list {append out [html::cell "" $x td]}
   append out [html::closeTag]

}

  1. Parse the CSV data

struct::queue rows foreach line [split $csvData "\n"] {

   csv::split2queue rows $line

}

  1. Generate the output

puts [subst {

   [table {

[tr [html::quoteFormValue [rows get]] {bgcolor="yellow"}] [html::while {[rows size]} { [tr [html::quoteFormValue [rows get]]] }]

   }]

}]</lang> Output:

<lang html4strict>

CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away! </angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</lang>

TUSCRIPT

<lang tuscript> $$ MODE TUSCRIPT MODE DATA $$ csv=* Character,Speech The multitude,The messiah! Show us the messiah! Brians mother,<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry> The multitude,Who are you? Brians mother,I'm his mother; that's who! The multitude,Behold his mother! Behold his mother! $$ htmlhead=* <!DOCTYPE html system> <html> <head> <title>Life of Brian</title> <style type="text/css"> th {background-color:orange} td {background-color:yellow}

</style></head><body>

$$ BUILD X_TABLE txt2html=* << < >> > $$ MODE TUSCRIPT file="brian.html" ERROR/STOP CREATE (file,FDF-o,-std-) csv=EXCHANGE (csv,txt2html) x=SPLIT (csv,":,:",row1,row2) ACCESS html: WRITE/ERASE/RECORDS/UTF8 $file s,html WRITE html htmlhead LOOP n,td1=row1,td2=row2 IF (n==1) THEN row=CONCAT ("")

ELSE

row=CONCAT ("")

ENDIF WRITE html row ENDLOOP

WRITE html "
",td1,"",td2,"
",td1,"",td2,"

</body></html>"

ENDACCESS/PRINT html </lang>

Output (source code)

<lang html4strict> <!DOCTYPE html system> <html> <head> <title>Life of Brian</title> <style type="text/css"> th {background-color:orange} td {background-color:yellow}

</style></head><body>

CharacterSpeech
The multitudeThe messiah! Show us the messiah!
Brians mother<angry>Now you listen here! He's not the messiah; he's a very naughty boy! Now go away!</angry>
The multitudeWho are you?
Brians motherI'm his mother; that's who!
The multitudeBehold his mother! Behold his mother!

</body></html>

</lang>

Output (rendered)

rendered by browser