2016年12月29日 星期四

[PHP] 使用 str_getcsv 處理 CSV 格式 (Google Docs export CSV format)

這議題已經很久,之前一直沒踩到什麼,直到處理 Google doc export the CSV format ...

大部分的分析都用:

foreach ( @explode("\n", file_get_content('https://docs.google.com/spreadsheets/d/.../export?format=csv...')) as $line ) {
$field = str_getcsv($line);
}


但是,若有 enclosure 的方式(例如 "...\n....\n") 就會出包啦,這在 http://php.net/manual/en/function.str-getcsv.php 網站上也有人提到,解法就是一樣用 str_getcsv 去切 $row:

$row = str_getcsv( file_get_content('https://docs.google.com/spreadsheets/d/.../export?format=csv...'), "\n");
foreach( $row as $line ) {
$field = str_getcsv($line);
}


但是,在處理 Google Docs 又發現切不對,最後發現改用 "\r\n" 即可 :P

$row = str_getcsv( file_get_content('https://docs.google.com/spreadsheets/d/.../export?format=csv...'), "\r\n");
foreach( $row as $line ) {
$field = str_getcsv($line);
}


Updated @ 2017/05/04
可以改用 tsv 輸出: $csv = explode("\n", @file_get_contents('https://docs.google.com/spreadsheets/d/.../export?format=tsv...'));
array_shift($csv); // remove header


foreach($csv as $line) {
$fields = str_getcsv($line, "\t");
print_r($fields);
}

2016年12月3日 星期六

[C] 使用 SQLite - sqlite3_bind_text 之 value 一直配對(綁定)錯誤

最近整理兩年前的程式,一直有空時會重構,由於時間拉太長了,我已不記得改過什麼,又懶得看 git log,就這樣東摸摸西摸摸,最後發現 bug 了。

有一張表:

CREATE TABLE IF NOT EXISTS test (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data TEXT NOT NULL
);


接著,不斷新增資料,並且透過 sqlite3_bind_text 維護:

INSERT OR IGNORE INTO test (data) VALUES (?),(?),(?)

然而,透過 sqlite3_bind_text 綁定時,一直出錯,起初以為是 UTF-8 問題,但早期程式也沒問題,透過觀察,發現記憶體不如預期,追了好一陣子,發現是 sqlite3_bind_text 的參數問題 Orz

https://www.sqlite.org/c3ref/c_static.html
Constants Defining Special Destructor Behavior

typedef void (*sqlite3_destructor_type)(void*);
#define SQLITE_STATIC      ((sqlite3_destructor_type)0)
#define SQLITE_TRANSIENT   ((sqlite3_destructor_type)-1)


原因在於我一直用 SQLITE_STATIC 啦,但我的結構已經是 std::vector<std::unordered_map<std::string, std::string>> values 這種稍微複雜的,並且透過 iterator 不斷變換資料,因此,必須改用 SQLITE_TRANSIENT 才對。

std::vector<std::unordered_map<std::string, std::string>> values;
std::vector<std::string> fields ({ "data" });
for (auto item : values) {
for (auto field: fields) {
if (item.find(field) != item.end()) {
if (SQLITE_OK != sqlite3_bind_text(stmt, i, item[field].c_str(), -1, SQLITE_TRANSIENT)) {
}
} else {
if (SQLITE_OK != sqlite3_bind_null(stmt, i)) {
}
}
}
}


最後還是埋了個 gtest 定期測試了。