最近お気に入りかもしれないアレステア・レナルズを読んでて、おなじみのネタが出てきたんである。異星人(違うけど)が名前を聞かれて「あなたたちの言葉では表現できない」と答えるやつ。
これ、よーく考えてみれば、外国人に名前を聞かれたときの日本人と同じ状況なんじゃないかと思った。
日本語の漢字は表意文字なわけで、これはつまり、日本人の脳の中にしかないクオリアの一部分をマークアップすることができる言語なわけで。名前の発音だけ答えてもちゃんと名前を教えたことにはなってない。
でもわりと気にせず使ってるんだよなー。
SFのファーストコンタクトネタの定番に、ふと疑問を感じた。
カーソルを動かしてるだけで固まったりするVisual Studioと戦っているのです。疲れます。
やってるんですよ仕事で。なんだか近ごろは、毎日Javaと.NETを往復してる感じ。先月まではPHP屋だった気がするんだけど。
Javaはあまり好きじゃないなあと思っていたんですが、.NETをやってみて考えかたが変わったね。.NETはJavaの比じゃーないくらい好きじゃない(笑)
いろんなプロパティをGUIで設定したり、GUIで画面をデザインしたり、そんな高級なツールの使いかた憶えるより言語憶えるほうが簡単じゃないかと……思うんだが、みんなは違うのかなあ。
あと、処理内容1ステップ単位で書いてある詳細設計書を渡されたりした。これ書いた奴はぜったい病気だと思った。もしあれが普通なのだとしたら、この業界が病気だ。
DB設計書をEXCELにおこして、そのときにテーブルのカラム名を日本語に翻訳してある。で、詳細設計書ではその日本語を使う。プログラマは詳細設計書を一行ずつ追いながら、日本語の項目名を逆にテーブル上のカラム名に変換して作業する。数百枚のEXCELシートを見ながらだ。
一時が万事そんな感じなのだ。画面や機能にはIDが振られている。オブジェクト名はなんちゃってハンガリアン記法で、でも設計書上は日本語で。すべて、一度変換しないと意味がとおらないようにできている。
そして、その設計書の内容は、よく読んでみれば「まともに設計すれば製造工数5分の1にできるなあ」と思うようなものだったり。
……ふつうに病んでると思う。業界経験が浅いから思うのかもしれないけど。
たぶん、仕事ってのは苦労することだと信じてる人たちがたくさんいるんだろうなあ。困ったなあ。
最近仕事がヒマだったせいか、くだらないPHPプログラムをいろいろ作ったんだけど。
ActiveRecordAnywhereクラスです。
といっても、そんなクラスは一度も呼ばないです。
DAOを超自動生成します。超自動です。
どれくらい自動かというと、1個requireするだけ。テーブル定義もなにも必要ないです。
あとはDBにテーブル作って、PDOオブジェクトを生成して、
$hoge = hoge::find($pdo , 'id = ?' , array($id));
いきなりそんなクラスがあるような顔でテーブル名のクラスのfind()という静的メソッドにWHERE句を投げると、hogeオブジェクトが配列で返ってきます。
あとは
$hogeName = $hoge$_lb0$_rb->name;
みたいにフィールドの値を取得したり
$hoge$_lb0$_rb->name = 'piyo';
とかでいろいろ値をセットしてから
$hoge$_lb0$_rb->update();
$hoge$_lb0$_rb->insert();
$hoge$_lb0$_rb->delete();
とかでDB更新みたいな。ふつうのアクティブレコードですね。
そんなクラスがあるような顔で継承とかもできるはず。
ちょーっと貼りつけるには長いですが。
<?php
function __autoload($class) {
$classBody = "
class {$class} extends ActiveRecordAnywhere {
protected \$table = '{$class}';
public static function find
(&\$pdo , \$where , \$params = array()) {
\$sql = \"SELECT * FROM {$class} WHERE \$where\";
\$sth = \$pdo->prepare(\$sql);
\$sth->execute(\$params);
\$ret = array();
while(\$row = \$sth->fetch(PDO::FETCH_ASSOC)) {
\$ret[] = new self(\$pdo , \$row);
}
return \$ret;
}
}";
eval($classBody);
}
class ActiveRecordAnywhere {
protected $pdo;
protected $row;
protected $where;
public $DBCharCode = 'UTF-8';
public function __construct(&$pdo , $row = array()) {
$this->pdo = &$pdo;
$this->row = $row;
$this->_init();
}
public function __set($aname , $avalue) {
$this->row[$aname] = mb_convert_encoding(
$avalue , $this->DBCharCode , mb_internal_encoding());
}
public function __get($aname) {
return mb_convert_encoding($this->row[$aname]
, mb_internal_encoding() , $this->DBCharCode);
}
public function insert() {
$sql = "INSERT INTO {$this->table}("
. join(" , " , array_keys($this->row)). ")"
. " VALUES (" . join(',' , array_fill(0 , $c , '?')) . ")";
$sth = $this->pdo->prepare($sql);
$sth->execute(array_values($this->row));
$this->_init();
$this->id = @$this->pdo->lastInsertId();
return $id;
}
public function update() {
$sql = "UPDATE {$this->table} SET ";
$set = array();
foreach($this->row as $k => $v) {
$set[] = "{$k} = ?";
$sqlParams[] = $v;
}
$sql .= join(',' , $set) . " WHERE " . $this->where;
$sth = $this->pdo->prepare($sql);
$sth->execute($sqlParams);
$affectedRows = $sth->rowCount();
$this->_init();
return $affectedRows;
}
public function delete() {
$sql = "DELETE FROM {$this->table} WHERE {$this->where}";
$sth = $this->pdo->prepare($sql);
$sth->execute();
$affectedRows = $sth->rowCount();
unset($this->row);
$this->_init();
return $affectedRows;
}
protected function _init() {
$wheres = array();
foreach($this->row as $k => $v) {
$wheres[] = "$k = " . $this->pdo->quote($v);
}
$this->where = join(' AND ' , $wheres);
}
}
?>
ぜんぜんちゃんと書いてないので、このままだといろいろ問題ある気がしますが。
ようするにあれだ。PHP5の謎機能__autoload()(←知らないクラスが呼ばれたら勝手に呼ばれる謎関数)で、クラスをeval生成です。
あと、知らないメンバ変数にアクセスすると勝手に呼ばれる謎メソッド__get()と__set()も使ってますねー。
冗談なのでちゃんと書いてないし、このまま使うのはやめたほうがいいけど。こんなんでも、データアクセス層がないよりはマシだと思うんだ……。
で、そんなことやっててすごく思うんだが、きっとこのサイト見る人にPHPプログラマはいないよな(笑) まあいっか。
昨日作ってみちゃってから「なんかコレおかしくね?」と思ったので細かくいじってみたり。
でもまあ、おかしいのはもとからだねというか技術がないねということで、いまあきらめたところ(ぉ
いろいろあったのだけど、ようするに使えないということがわかった……。忘れないように書いとく。
PHPにfgetcsv()という便利関数があって。ファイルをCSVとして一行とりだして、各列の値を配列に入れて返すというものなんだが。
PHP4のはじめのころは、バグだらけなので有名だったらしいのだけど、さすがにPHP5.2なら大丈夫だろうと思ったら。しかしダメ。
マルチバイト文字の2バイト目に'\'があると、直後のダブルクォーテーションをエスケープするという、少なくともEXCELの仕様にはない独自解釈をするらしく。つまりSJISのファイルを読ませるとうまくいかない。
(ただし、EXCELがCSVをどう解釈するかという仕様は公開されてなさげ)
じゃあEUCに変換してから読ませれば、いちおうは大丈夫なんだけど、もちろん機種依存文字が化ける。 ←(これは指摘されたのでいちおう修正。SJIS-winからEUCJP-winとかUTF-8とかに変換すれば使える。けどめどい)
だいだいCSVを使うときというのはEXCELからくるデータなわけで。文字コードはSJISだし、機種依存文字も使いまくりがあたりまえだ。それがいけないのではあるけど、しかし対応するしかないし。
もしどうしても使うなら、あらかじめ機種依存文字を安全な文字列に変換して、EUCにして保存し、そのあとでファイルを開いてfgetcsv()するとか?(やる気にならないから試してない)もちろん、そんなことするなら自分で処理を書いたほうがいい。 ←(これもSJIS-winとか指定すれば特別な変換はいらない)
というわけで。もう二度と使わないぞ。
というわけで、できるだけExcelの動きに近づけてみた関数を作ってみたんだ。
調べてみると、セル中でダブルクォートが出てきたときとか、あんたほんとにそれでいいのかと思う動きをするけど、あくまでそんなExcelを再現しようとしてみる。
function fgetExcelCSV(&$fp , $length = null
, $delimiter = ',' , $enclosure = '"') {
$line = fgets($fp);
if($line === false) {
return false;
}
$bytes = preg_split('//' , trim($line));
array_shift($bytes);array_pop($bytes);
$cols = array();
$col = '';
$isInQuote = false;
while($bytes) {
$byte = array_shift($bytes);
if($isInQuote) {
if($byte == $enclosure) {
if($bytes[0] == $enclosure) {
$col .= $byte;
array_shift($bytes);
} else {
$isInQuote = false;
}
} else {
$col .= $byte;
}
} else {
if($byte == $delimiter) {
$cols[] = $col;
$col = '';
} elseif($byte == $enclosure && $col == '') {
$isInQuote = true;
} else {
$col .= $byte;
}
}
while(!$bytes && $isInQuote) {
$col .= "\n";
$line = fgets($fp);
if($line === false) {
$isInQuote = false;
} else {
$bytes = preg_split('//' , trim($line));
array_shift($bytes);array_pop($bytes);
}
}
}
$cols[] = $col;
return $cols;
}
つまりなにがいいたいかというと、
っていうかそんなの例外ケースなので、やる必要はまったくないけど(笑)
[2007.10.27 11:22]anonymous :
CSVファイルをSJIS-winからUTF-8に変換してから、
fgetcsv()を使用し、UTF-8からSJIS-winに戻せば
機種依存文字等も壊れないけどね。
※SJIS→EUCだと壊れる。
[2007.10.27 21:47]てらしま :
実際仕事ではそういうことやりましたが(笑) けっきょく、それでもExcelと動作が違っていると、いつか不具合といわれても不思議はないのが現実なんですよねえ。
[2007.10.28 01:12]てらしま :
というわけで、まあ指摘いただいたのでいちおう上の内容を修正しときます。
[2007.11.02 12:00]anonymous :
fgetExcelCSV()って、セル内に改行がある場合に対応してないのでは?
fgetcsv() はセル内に改行がある場合に対応してるけど…。
[2007.11.02 23:18]てらしま :
対応してるはずです。がやってみてうまくいかなかったら、なおすなりなおせというなりしてください。
うえき -2009/07/13 18:16
わかりやすいソースですね。このソース(fgetExcelCSV)のライセンスを教えてください。
サイトで使わせていただきたいと思っています。
てらしま -2009/07/13 19:16
……考えてもいなかったです。
なにをどうされてもかまわないです。とりあえずNYSL0.9982にしたがいます、ということでおねがいします。
うえき -2009/07/14 09:59
ありがとうございます。使わせていただきます。
Motty -2012/08/20 16:35
今だに fgetcsv に悩まされていました。
このページに救われました!
fgetExcelCSV使わせて頂きます。
有難うございます。
てらしま -2012/08/21 16:07
いやその、読んでいただいたのは嬉しいですが。
これはもう何年も前に書いたもので、しかも半分冗談の書き殴りコードです。使用はおすすめしません。
もっと新しくていいものがあるので、そちらを探してください。