Translate CSV To HTML
January 15, 2013
The text-file database library makes this task simple: We write a write-html-rec function similar to the other writers, then use for-each-port to perform the conversion. Here’s write-html-rec:
(define (write-html-record rec)
(display "<tr>")
(for-each
(lambda (field)
(display "<td>")
(display field)
(display "</td>"))
rec)
(display "</tr>")
(newline))
Each record forms a single line, with an initial <tr> and final </tr>. Each field within the record is written with an initial <td> and final </td>. This function is called in the csv->html function, which reads from the current-input-port and writes to the current-output-port:
(define (csv->html)
(display "<table>")
(newline)
(for-each-port
read-csv-record
write-html-record)
(display "</table>")
(newline))
Here we give the initial <table> and final </table>, then use for-each-port to structure the processing.
We use the same emp.data sample file as the essay, with help from the string-join function of the Standard Prelude:
(define emp-data-csv
(string-join #\newline
'("Beth,12.75,0,mfg"
"Dan,8.50,10,sales"
"Kathy,11.40,30,sales"
"Mark,12.75,40,mfg"
"Mary,7.50,20,mfg"
"Susie,10.30,25,acctg")))
That can be converted to HTML like this:
(define emp-data-html
(with-input-from-string
emp-data-csv
(lambda ()
(with-output-to-string
(lambda ()
(csv->html))))))
The resulting output, folded at newlines, contains all the markup for an HTML table:
<table>\n
<tr><td>Beth</td><td>12.75</td><td>0</td><td>mfg</td></tr>\n
<tr><td>Dan</td><td>8.50</td><td>10</td><td>sales</td></tr>\n
<tr><td>Kathy</td><td>11.40</td><td>30</td><td>sales</td></tr>\n
<tr><td>Mark</td><td>12.75</td><td>40</td><td>mfg</td></tr>\n
<tr><td>Mary</td><td>7.50</td><td>20</td><td>mfg</td></tr>\n
<tr><td>Susie</td><td>10.30</td><td>25</td><td>acctg</td></tr>\n
</table>\n
Rendered in a browser, it looks like this:
| Beth | 12.75 | 0 | mfg |
| Dan | 8.50 | 10 | sales |
| Kathy | 11.40 | 30 | sales |
| Mark | 12.75 | 40 | mfg |
| Mary | 7.50 | 20 | mfg |
| Susie | 10.30 | 25 | acctg |
Our HTML is purposely simple-minded. You are welcome to add borders or other formatting, to treat the first line of the file as column headers, or make other changes to suit your needs.
You can run the program at http://programmingpraxis.codepad.org/ug3oKyMJ.
[…] today’s Programming Praxis exercise, our goal is to translate a csv file to an HTML table. Let’s get […]
My Haskell solution (see http://bonsaicode.wordpress.com/2013/01/15/programming-praxis-translate-csv-to-html/ for a version with comments):
import Text.CSV import Text.Printf toTable :: [[String]] -> String toTable = wrap "table" unlines . wrap "tr" concat $ wrap "td" id id where wrap :: String -> ([b] -> String) -> (a -> b) -> [a] -> String wrap tag combine f xs = printf "<%s>%s</%s>" tag (combine $ map f xs) tag main :: IO () main = putStrLn . either show toTable =<< parseCSVFromFile "test.csv"[…] Question is from here: […]
Java solution here.
Answer in awk (here)[http://psykotedy.tumblr.com/post/40602728216/programming-praxis-convert-csv-to-html].
Sorry for the sloppy comment, I don’t know the markup required for the comments here.
[…] Pages: 1 2 […]
Ruby solution
"<table><tr><td>#{csv.split("\n").join('</td></tr><tr><td>').split(',').join('</td><td>')}</td></tr></table>"Doesn’t seem anyone other than Programming Praxis dealt with quoted strings
(which, granted, isn’t necessarily a part of ‘official’ CSV files). It does
make the code quite a bit more interesting if you do. :)
Anyways, here’s my short (ugly) version that doesn’t deal with quoted
strings:
(require racket/string) (define (csv->html csv) (printf "<table>\n") (for ([line (in-list (string-split csv "\n"))]) (printf " <tr>\n") (for ([item (in-list (string-split line ","))]) (printf " <td>~s</td>\n" item)) (printf " </tr>\n")) (printf "</table>\n"))And here’s a longer (more awesome!) version that correctly deals with
quotes and translates what it can to Scheme values (numbers, symbols,
#t/#f, etc): blog post
[…] post is a Python solution to a programming challenge from Programming Praxis. The challenge is to read a csv file and convert it into an html […]
Here’s a solution in PHP:
<?php
define("DIRECTORY", "./");
if (file_exists(DIRECTORY."x.txt")) {
$xfile = fopen(DIRECTORY."x.txt", "r");
$xcontent = fgetcsv($xfile);
echo "
“;
foreach ($xcontent as $xentry) {
echo “”.$xentry.””;
}
echo ”
“;
} else die(“The file does not exist.”);
?>
Quotation marks delineated with a q:
<?php
define("DIRECTORY", "./");
if (file_exists(DIRECTORY."x.txt")) {
$xfile = fopen(DIRECTORY."x.txt", "r");
$xcontent = fgetcsv($xfile);
echo q
q;
foreach ($xcontent as $xentry) {
echo qq.$xentry.qq;
}
echo q
q;
} else die(“The file does not exist.”);
?>
Third time is NOT a charm… :/
CSV To HTML
Using JSP
CSV To HTML
Python 3.3 solution. Uses the standard library csv to read the file and elementtree to build the table as an xml document. The tostring method takes care of properly escaping characters like <, if any, in the file.
import csv
import xml.etree.ElementTree as et
def csv2html(filelike_obj, id_=None):
table = et.Element(‘table’, {‘id’:id_} if id_ else {})
for n, line in enumerate(csv.reader(filelike_obj)):
row = et.SubElement(table, ‘tr’)
if n&1: row.set(‘class’, ‘odd’)
for item in line:
col = et.SubElement(row, ‘td’)
col.text = item
return et.tostring(table, method=’html’).decode()
Not sure what happened to the formatting above.
import csv import xml.etree.ElementTree as et def csv2html(filelike_obj, id_=None): table = et.Element('table', {'id':id_} if id_ else {}) for n, line in enumerate(csv.reader(filelike_obj)): row = et.SubElement(table, 'tr') if n&1: row.set('class', 'odd') for item in line: col = et.SubElement(row, 'td') col.text = item return et.tostring(table, method='html').decode()C#:
static string Csv2Html(string filePath) { return "<table>" + new StreamReader(filePath) .ReadToEnd() .Split('\n') .Aggregate(string.Empty, (body, record) => body + "<tr>" + record.Trim() .Split(',').Aggregate(string.Empty, (w, s) => w + "<td>" + s + "</td>") + "</tr>\n") + "</table>"; }C#:
namespace CsvToHtml { using System.IO; using System.Text; using System.Web; internal class Program { #region Methods private static void Main(string[] args) { var csv = ReadData(args[0]); var htmlEncoded = HttpUtility.HtmlEncode(csv); var html = new StringBuilder("<table><tr><td>"); html.Append(htmlEncoded.Replace(",", "</td></tr><tr><td>")); html.Append("</td></tr></table>"); } private static string ReadData(string fileName) { var str = new StringBuilder(); using (var stream = new StreamReader(fileName)) { var buffer = new char[1024]; while (!stream.EndOfStream) { var charactersRead = stream.ReadBlock(buffer, 0, 1024); str.Append(new string(buffer, 0, charactersRead)); } } return str.ToString(); } #endregion } }erdalkiran, few comments on your solution:
1. It generates 1-column table – for every comma in the source you produce a new row in the output. Instead it should produce a cell for every comma and a row for each newline.
2. I see no reason for manual buffered read in this case – you don’t process those chunks one by one, and in the end you still have a full raw string in memory, so why not use something like StreamReader.ReadToEnd() for simplicity?
3. I can’t see a practical reason for using StringBuilder to store the final output here. StringBuilder might save you memory allocations and some processor cycles if you have lots of string operations, true. But you only have 2 appends. Your code will still likely force a StringBuilder to allocate memory for its internal storage, perhaps more times than it would take to allocate 3 usual immutable strings in the first place.
Thus, I would suggest to implement your approach something like this:
static string Csv2Html(string filePath) { return "<table><tr><td>" + new StreamReader(filePath) .ReadToEnd() .Replace("\n", "</td></tr><tr><td>") //new row for each newline .Replace("\r", string.Empty) //getting rid of newline leftovers, just in case .Replace(",", "</td><td>") //new cell for each comma + "</td></tr></table>"; }