私自身が表題の問題を解いた時のプログラムについて解説します。
問題の詳細は「CSVからHTMLに変換しよう」(CodeIQ)を参照してください。
あなたはCSVフォーマットのデータを、テーブルタグを用いてHTML化する仕事を任されました。
もちろんこのような処理を行うツールはありますが、今後カスタマイズすることを前提に自動化したいとのこと。
そこで、CSVからHTMLに置換するプログラムを作ることになりました。
求められるプログラムの前提条件は、以下の通りとなります。
標準入力から、CSVフォーマット(wikipedia参照)のデータが送られる
なおCSVはコンマ「,」(U+002C) 区切りで、文字列フィールドはダブルクォート「"」(U+0022)で囲まれる場合がある
CSVの文字列フィールドに、改行を含む制御文字や、全角文字は含まれないものとする
CSVにはヘッダ行が必ず存在し、カラム名を表している
CSVを読み込み、table, tr, th, tdタグを用いてHTMLに変換すること
このときカラム名はthタグで囲み、各レコードのフィールドはtdタグで囲むこと
文字列フィールドは文字実体参照(wikipedia参照)を用いてエスケープ処理をすること
ただし、<(小なり記号)、>(大なり記号)、&(アンパサンド)のみの対応でよいものとする
なお出力するHTMLは部分的なものであり、テーブル関連タグ以外は用いないこと
また、出力するHTMLに整形のための改行は含めないこと
変換したHTMLを標準出力に返すこと
以下、置換例となります。
【入出力サンプル】
標準入力
"x","y"
1,2
標準出力
<table><tr><th>x</th><th>y</th></tr><tr><td>1</td><td>2</td></tr></table>
実際の分析業務において、データをHTMLに変換して可視化するという仕事は、決して珍しくありません。
その背景には、CSSを用いたレイアウトなど、様々な応用が利くため、可視化手段として扱いやすいという理由があります。
是非挑戦してみてください!
【問題】
標準入力から、CSVフォーマットでデータが送られます。
このデータをテーブルタグを用いてHTMLに変換し、その結果を標準出力に返してください。
なお、文字列フィールドのエスケープ処理も忘れずに。
※ご利用のブラウザによって、エスケープ処理された文字が変換されて表示される箇所があります
Rubyで解答しています。
#!/usr/bin/ruby require 'csv' def parseRow(line) replaced = line.gsub("&", "&").gsub("<", "<").gsub(">", ">") return CSV.parse_line(replaced) end def getTableRow(row, tag) ret = "<tr>" for r in row ret += "<" + tag + ">" + r.gsub('"', '') + "</" + tag + ">" end return ret + "</tr>" end def printTable(rows) html = "<table>" rows.each_with_index{|row, i| if i == 0 then html += getTableRow(row, "th") else html += getTableRow(row, "td") end } html += "</table>" puts html end # main rows = [] while line = gets line.strip! if line.empty? then next end rows << parseRow(line) end printTable(rows)
全部自力でやろうとするとCSVのパースが面倒臭いです。
今回はRubyだったので便利なライブラリを使えたため簡単でした。
基本的には入力値を1行ずつパースして配列にし、1行目は<ht>で囲い、それ以外は<td>で囲うというだけです。面倒臭いのはCSVのパースです。
以前、別の問題で(確かJavaで)CSVのパース処理を書いたのですが、今回はRubyだったのでCSVのライブラリがあるんじゃないか、と思って調べたらあったのでそれを使いました。なので面倒な部分が全部なくなっています。
1行ずつparseRow()に渡して配列を取得します。
全ての入力値を配列に変えたら、printTable()でHTML文字列を印字します。
入力値1行のエスケープが必要な部分をエスケープし、CSV#parse_line()で配列に変換して返すだけです。
ちょっとした工夫が&を最初に変換してしまうことです。
<table>〜</table>までを印字します。
テーブルの各行を作るのはgetTableRow()で、この関数はgetTableRow()に1行分のデータを渡して返却値を連結することと、前後に<table>と</table>をつけて印字することだけです。
1行目だけは<th>なのでgetTableRow()に"th"を指定し、それ以外は"td"を指定することでタグを変更します。
rowは1行分のデータの配列、tagはデータ1つを囲むタグの文字列("th"か"td")です。
データ1個ずつを<th>か<td>で囲んで連結し、前後に<tr>の開始タグと終了タグをつけるだけです。CSV#parse_line()で作った文字列が"と"で囲まれているのでそれは消しています。
CSVのパースを自分で書くと面倒臭いのは"hoge,fuga"みたいな項目を考慮しなければいけないからです。もっと面倒なのはダブルクォートのエスケープで"""hoge"""を考慮しなければいけません(バックスラッシュでエスケープの処理系もあるらしい)。
結局、これらを考慮すると1文字ずつ処理しなければいけなくて非常に面倒です。