CSV to HTML translation

Revision as of 21:53, 3 June 2011 by rosettacode>Paddy3118 (→‎{{header|Lua}}: Marked incomplete due to no escaping of '<' in '<angry' and '</angry')

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

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

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:

<lang html>

@@TABLE@@ @@END_TABLE@@
@_WEB_ESCAPE:CHAR_@ @_WEB_ESCAPE:SPEECH_@

</lang>

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>

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>

C++

<lang cpp>#include <string>

  1. include <boost/regex.hpp>
  2. include <iostream>

std::string csvToHTML( const std::string & ) ;

int main( ) {

  std::string text = "Character,Speech\n" 
                           "The multitude,The messiah! Show us the messiah!\n" 

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

 std::cout << csvToHTML( text ) ;
 return 0 ;

}

std::string csvToHTML( const std::string & csvtext ) {

  //the order of the regexes and the replacements is decisive!
  std::string regexes[ 5 ] = { "<" , ">" , "^(.+?)\\b" , "," , "\n" } ;

const char* replacements [ 5 ] = { "<" , ">" , " $1" , "", "\n" } ;

  boost::regex e1( regexes[ 0 ] ) ; 
  std::string tabletext = boost::regex_replace( csvtext , e1 ,
    replacements[ 0 ] , boost::match_default | boost::format_all ) ;
  for ( int i = 1 ; i < 5 ; i++ ) {
     e1.assign( regexes[ i ] ) ;
     tabletext = boost::regex_replace( tabletext , e1 , replacements[ i ] , boost::match_default | boost::format_all ) ;
  }

tabletext = std::string( "

\n" ) + tabletext ; tabletext.append( "

\n" ) ;

  return tabletext ;

}</lang> 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>

CoffeeScript

Works with: node.js

<lang coffeescript>String::__defineGetter__ 'escaped', () -> this.replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') // rosettacode doesn't like "

text = 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!

lines = (line.split ',' for line in text.split /[\n\r]+/g)

header = lines.shift()

console.log """

<thead>

</thead> <tbody> """

for line in lines [character, speech] = line console.log """

"""

console.log """ </tbody>

#{header[0]} #{header[1]} #{character} #{speech.escaped}

"""</lang>

Output:

<table cellspacing="0">
  <thead>
    <th scope="col">Character</th>
    <th scope="col">Speech</th>
  </thead>
  <tbody>
    <th scope="row">The multitude</th>
    <td>The messiah! Show us the messiah!</td>
    <th scope="row">Brians mother</th>
    <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>
    <th scope="row">The multitude</th>
    <td>Who are you?</td>
    <th scope="row">Brians mother</th>
    <td>I'm his mother; that's who!</td>
    <th scope="row">The multitude</th>
    <td>Behold his mother! Behold his mother!</td>
  </tbody>
</table>

Delphi

This solution solves both the basic and extra credit tasks.

<lang Delphi>

program csv2html;

{$APPTYPE CONSOLE}

uses

 SysUtils,
 Classes;

const

 // Carriage Return/Line Feed
 CRLF    = #13#10;
 // The CSV data
 csvData =
 'Character,Speech'+CRLF+
 'The multitude,The messiah! Show us the messiah!'+CRLF+
 'Brians mother,<angry>Now you listen here! Hes not the messiah; hes a very naughty boy! Now go away!</angry>'+CRLF+
 'The multitude,Who are you?'+CRLF+
 'Brians mother,Im his mother; thats who!'+CRLF+
 'The multitude,Behold his mother! Behold his mother!';
 // HTML header
 htmlHead =
 '<!DOCTYPE html'+CRLF+
 'PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"'+CRLF+
 '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'+CRLF+
 '<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">'+CRLF+
 '<head>'+CRLF+
 '<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />'+CRLF+
 '<title>CSV-to-HTML Conversion</title>'+CRLF+
 '<style type="text/css">'+CRLF+
 'body {font-family:verdana,helvetica,sans-serif;font-size:100%}'+CRLF+
 'table {width:70%;border:0;font-size:80%;margin:auto}'+CRLF+
 'th,td {padding:4px}'+CRLF+
 'th {text-align:left;background-color:#eee}'+CRLF+
 'th.c {width:15%}'+CRLF+
 'td.c {width:15%}'+CRLF+
 '</style>'+CRLF+
 '</head>'+CRLF+
 '<body>'+CRLF;
 // HTML footer
 htmlFoot =
 '</body>'+CRLF+
 '</html>';


{ Function to split a string into a list using a given delimiter } procedure SplitString(S, Delim: string; Rslt: TStrings); var

 i: integer;
 fld: string;

begin

 fld := ;
 for i := Length(S) downto 1 do
   begin
     if S[i] = Delim then
       begin
         Rslt.Insert(0,fld);
         fld := ;
       end
       else
        fld := S[i]+fld;
   end;
 if (fld <> ) then
     Rslt.Insert(0,fld);

end;


{ Simple CSV parser with option to specify that the first row is a header row } procedure ParseCSV(const csvIn: string; htmlOut: TStrings; FirstRowIsHeader: Boolean = True); const

rowstart = ''; rowend = ''; cellendstart = ''; hcellendstart = ''; hrowstart = ''; hrowend = ''; var tmp,pieces: TStrings; i: Integer; begin // HTML header htmlOut.Text := htmlHead + CRLF + CRLF; // Start the HTML table htmlOut.Text := htmlOut.Text + '

' + CRLF; // Create stringlist tmp := TStringList.Create; try // Assign CSV data to stringlist and fix occurences of '<' and '>' tmp.Text := StringReplace(csvIn,'<','<',[rfReplaceAll]); tmp.Text := StringReplace(tmp.Text,'>','>',[rfReplaceAll]); // Create stringlist to hold the parts of the split data pieces := TStringList.Create; try // Loop through the CSV rows for i := 0 to Pred(tmp.Count) do begin // Split the current row SplitString(tmp[i],',',pieces); // Check if first row and FirstRowIsHeader flag set if (i = 0) and FirstRowIsHeader then // Render HTML htmlOut.Text := htmlOut.Text + hrowstart + pieces[0] + hcellendstart + pieces[1] + hrowend + CRLF else htmlOut.Text := htmlOut.Text + rowstart + pieces[0] + cellendstart + pieces[1] + rowend + CRLF; end; // Finish the HTML table and end the HTML page htmlOut.Text := htmlOut.Text + '

' + CRLF + htmlFoot;

   finally
     pieces.Free;
   end;
 finally
   tmp.Free;
 end;

end;


var

 HTML: TStrings;

begin

 // Create stringlist to hold HTML output
 HTML := TStringList.Create;
 try
   Writeln('Basic:');
   Writeln();
   // Load and parse the CSV data
   ParseCSV(csvData,HTML,False);
   // Output the HTML to the console
   Writeln(HTML.Text);
   // Save the HTML to a file (in application's folder)
   HTML.SaveToFile('csv2html_basic.html');
   Writeln();
   Writeln('=====================================');
   Writeln();
   HTML.Clear;
   Writeln('Extra Credit:');
   Writeln();
   // Load and parse the CSV data
   ParseCSV(csvData,HTML,True);
   // Output the HTML to the console
   Writeln(HTML.Text);
   // Save the HTML to a file (in application's folder)
   HTML.SaveToFile('csv2html_extra.html');
   Writeln();
   Writeln('=====================================');
 finally
   HTML.Free;
 end;
 // Keep console window open
 Readln;


end.

</lang>


Basic output: <lang Delphi> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> <title>CSV-to-HTML Conversion</title> <style type="text/css"> body {font-family:verdana,helvetica,sans-serif;font-size:100%} table {width:70%;border:0;font-size:80%;margin:auto} th,td {padding:4px} th {text-align:left;background-color:#eee} th.c {width:15%} td.c {width:15%} </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>

Extra credit output: <lang Delphi> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" /> <title>CSV-to-HTML Conversion</title> <style type="text/css"> body {font-family:verdana,helvetica,sans-serif;font-size:100%} table {width:70%;border:0;font-size:80%;margin:auto} th,td {padding:4px} th {text-align:left;background-color:#eee} th.c {width:15%} td.c {width:15%} </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>

Go

For extra credit, version below takes -h as a command line option to produce a table with special formating of the header row. <lang go>package main

import (

   "flag"
   "fmt"
   "os"
   "strings"
   "template"

)

var 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!`

type Cell struct {

   Text string

}

type Fancy struct {

   Headings []Cell
   Rows     [][]Cell

}

var SimpleTemplate = `

{.repeated section @} {.repeated section @}{.end}
   {.end}
{Text|html}

`

var FancyTemplate = `

{.repeated section Headings}{.end}
   {.repeated section Rows}
{.repeated section @}{.end}
   {.end}
{Text|html}
{Text|html}

`

func main() {

   // look for headings command line option
   headings := flag.Bool("h", false, "format first row as column headings")
   flag.Parse()
   // split csv data in a data structure that template can use
   var sd [][]Cell
   for _, r := range strings.Split(csv, "\n", -1) {
       var row []Cell
       for _, c := range strings.Split(r, ",", -1) {
           row = append(row, Cell{c})
       }
       sd = append(sd, row)
   }
   var data interface{}
   var ct *template.Template
   if !*headings {
       ct = template.MustParse(SimpleTemplate, nil)
       data = sd
   } else {
       ct = template.MustParse(FancyTemplate, nil)
       data = Fancy{sd[0], sd[1:]}
   }
   // open output file
   hf, err := os.Create("table.html")
   if err != nil {
       fmt.Println(err)
       return
   }
   defer hf.Close()
   // apply template to data
   ct.Execute(hf, data)

}</lang> Standard output in table.html: <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>

Output with -h: (Notice tag on first row. Otherwise output is identical.)

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

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:

<lang html><html>

<head> <title>Basic</title> <style type="text/css"> td {background-color:#ddddff; } thead td {background-color:#ddffdd; text-align:center; } </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>


Appearance as rendered in Google Chrome.

Extra Credit output:

<lang html><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>

<thead>

</thead> <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!

</body>

</html></lang>


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:

<lang html><html>
 <head>
   <title>Basic</title>
   <style type='text/css'>
                   td {background-color:#ddddff; }
                   thead td {background-color:#ddffdd; text-align:center; }
               </style>
 </head>
 <body>
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!
 </body>
</html></lang>

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:

<lang html><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>
<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!
 </body>
</html></lang>

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

Haskell

Simple solution <lang haskell>splitOn :: Char -> String -> [String] splitOn _ [] = [""] splitOn delim (x:xs)

   | x == delim = "" : rest
   | otherwise  = (x:head rest):tail rest
   where rest = splitOn delim xs

htmlEscape :: String -> String htmlEscape = concatMap escapeChar

             where escapeChar '<' = "<"
                   escapeChar '>' = ">"
                   escapeChar '&' = "&"
                   escapeChar '"' = """ --"
                   escapeChar c   = [c]

toHtmlRow :: [String] -> String

toHtmlRow [] = "" toHtmlRow cols = let htmlColumns = foldr1 (++) $ map toHtmlCol cols in "\n" ++ htmlColumns ++ "" where toHtmlCol x = " " ++ htmlEscape x ++ "\n"

csvToTable :: String -> String csvToTable csv = let rows = map (splitOn ',') $ lines csv

                    html = unlines $ map toHtmlRow rows

in "

\n" ++ html ++ "

"

main :: IO () main = interact csvToTable</lang>

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>

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>

Lua

This example is incomplete. No escaping of '<' in '<angry' and '</angry' Please ensure that it meets all task requirements and remove this message.

<lang lua>FS = "," -- field separator

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! ]]

html = { "

" } for line in string.gmatch( csv, "(.-\n)" ) do str = "" for field in string.gmatch( line, "(.-)["..FS.."?\n?]" ) do str = str .. ""
   end
str = str .. "" html[#html+1] = str; end html[#html+1] = "
" .. field .. "

"

for _, line in pairs(html) do

   print(line)

end</lang>

<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><td></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>

MATLAB

This example is incomplete. No escaping of '<' in '<angry' and '</angry' Please ensure that it meets all task requirements and remove this message.

The easiest way to import csv data into MATLAB is to save it in a csv file, then import the data using the "uiimport -file" command. The result of which will be a cell array with each entry being a string delimited by the newline character.

Example: <lang MATLAB>>> data

data =

   'Character,Speech'
   'The multitude,The messiah! Show us the messiah!'
   [1x109 char]
   'The multitude,Who are you?'
   'Brians mother,I'm his mother; that's who!'
   'The multitude,Behold his mother! Behold his mother!'</lang>

The other way is to copy and paste the data, but you will have to manually add all of the newline characters and string quotations your self. This is messy and infeasible for large amounts of data, but it is still a valid input.

Example: <lang MATLAB>>> csvData = ['Character,Speech' sprintf('\n')... 'The multitude,The messiah! Show us the messiah!' sprintf('\n')... 'Brians mother,<angry>Now you listen here! Hes not the messiah; hes a very naughty boy! Now go away!</angry>' sprintf('\n')... 'The multitude,Who are you?' sprintf('\n')... 'Brians mother,Im his mother; thats who!' sprintf('\n')... 'The multitude,Behold his mother! Behold his mother!']

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!</lang>

So, to be able to accept both type of inputs, the function "csvToHTHML()" tests to see if the input is a string as in example 2. If the input is a string it converts the data to a cell array of strings formatted in the exact same way MATLAB formats the first example. The output of the function will be a properly formatted HTML table, which includes the "<THEAD>" around the first row of data so that special formatting can be applied to the column titles.

<lang MATLAB>function htmlOutput = csvToHTML(csvData)

   if ischar(csvData)
      newlineIndex = find( (csvData == char(10)) ); %find all newline characters
      text = cell( numel(newlineIndex),1 ); %preallocate space for processed data
      
      for i = (numel(newlineIndex):-1:1) %iterate backwards
          text{i} = csvData(newlineIndex(i)+1:end);
          csvData(newlineIndex(i):end) = []; %delete the newline and everything after it
      end
      
      csvData = text;
      clear text;
   end
         

htmlOutput = '

'; for i = (1:numel(csvData)) if i == 1 %If first entry, then include <thead> csvData{i} = [char(10) char(9) '<thead>' char(10) char(9) char(9) '' char(10) char(9) '</thead>' char(10)]; else %Otherwise, the data is a regular table row csvData{i} = [char(9) '' char(10) char(9) char(9) '' char(10) char(9) '' char(10)]; end %if %Convert each comma to its HTML equivalent for j = fliplr(find( (csvData{i} == ',') )) csvData{i} = [csvData{i}(1:j-1) '' char(10) char(9) char(9) '
'... csvData{i} '
' csvData{i}... '
' csvData{i}(j+1:end)];
       end %for
       
       %We could make this faster by preallocating htmlOutput but that is
       %more work than necessary to provide a basic solution
       htmlOutput = [htmlOutput csvData{i}];
       
   end %for
htmlOutput = [htmlOutput '

'];

end %csvToHTML</lang>

Ouput: <lang MATLAB>>> csvToHTML(data)

ans =

<thead></thead>
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>

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

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

Powershell


Simple solution

<lang Powershell> Import-Csv -Path .\csv_html_test.csv | ConvertTo-Html -Fragment | Out-File .\csv_html_test.html </lang> OutPut : <lang html>

<colgroup> <col/> <col/> </colgroup>
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 Powershell> $htmlformat = '<title>Csv to Html</title>' $htmlformat += '<style type="text/css">' $htmlformat += 'BODY{background-color:#663300;color:#FFCC00;font-family:Arial Narrow,sans-serif;font-size:17px;}' $htmlformat += 'TABLE{border-width: 3px;border-style: solid;border-color: black;border-collapse: collapse;}' $htmlformat += 'TH{border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color:#663333}' $htmlformat += 'TD{border-width: 1px;padding: 8px;border-style: solid;border-color: black;background-color:#660033}' $htmlformat += '</style>'

Import-Csv -Path .\csv_html_test.csv | ConvertTo-Html -Head $htmlformat -Body '

Csv to Html

' | Out-File .\csv_html_test.html

Invoke-Expression .\csv_html_test.html </lang> OutPut : <lang html> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Csv to Html</title><style type="text/css">BODY{background-color:#663300;color:#FFCC00;font-family:Arial Narrow,sans-serif;font-size:17px;}TABLE{border-width: 3px;border-style: solid;border-color: black;border-collapse: collapse;}TH{border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color:#663333}TD{border-width: 1px;padding: 8px;border-style: solid;border-color: black;background-color:#660033}</style> </head><body>

Csv to Html

<colgroup> <col/> <col/> </colgroup>
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>

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 :

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

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 :

<lang html>

<THEAD> </THEAD> <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>

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