2012年8月4日 星期六

An Introduction To The SQLite C/C++ Interface


為了能夠用SQL將資料由資料庫撈出,我們必須認識兩個主要的Object:
  1. The database connection object:sqlite3
  2. 執行sqlite3_open()之後,就會得到該object,感覺類似open之後的file descriptor。
  3. The prepared statement object:sqlite3_stmt
  4. 把SQL轉成bytecode,但是還沒真正被執行。請參考Compiling An SQL Statement/To execute an SQL query, it must first be compiled into a byte-code program using one of these routines.
    An instance of this object represents a single SQL statement. This object is variously known as a "prepared statement" or a "compiled SQL statement" or simply as a "statement".


一個常見的SQLite pattern為:
  1. sqlite3_open():取得database connection object。
  2. sqlite3_preapre() :取得prepared statement object。
  3. sqlite3_step():執行prepared statement object。
  4. sqlite3_column_<type>():將sqlite回傳回來的資料,轉成對應的type取出。
  5. sqlite3_finalize():釋放prepared statement object。
  6. sqlite3_close():釋放database connection object。


#include <iostream>
#include <sqlite3.h>
#include <cstdlib>
#include <assert.h>

using namespace std;

int main(int argc, char** argv)
{
  sqlite3 *conn;
  sqlite3_stmt *statement; // SQL Statement Object
  int ret = 0;
  int cols;

  // This routine opens a connection to an SQLite database file
  //  and returns a database connection object.
  ret = sqlite3_open_v2("hello.db", &conn, SQLITE_OPEN_READONLY, NULL);
  if (ret) {
    cout << "can not open database\n";
    exit(0);
  }

  ret = sqlite3_prepare_v2(conn, "select * from hello", -1, &statement, NULL);
  if (ret != SQLITE_OK) {
    cout << "We did not get any data\n";
    exit(0);
  }

  cols = sqlite3_column_count(statement);

  for (int col = 0; col < cols; col++) {
    cout << " " << sqlite3_column_name(statement, col);
  };
  cout << endl;

  while (true) {
    ret = sqlite3_step(statement);
    if (ret == SQLITE_ROW) {
      for (int col = 0; col < cols; col++) {
        switch (sqlite3_column_type(statement, col)) {
          case SQLITE_INTEGER:
            cout << " " << sqlite3_column_int(statement, col) << " ";
            break;
          case SQLITE_FLOAT:
            cout << " " << sqlite3_column_double(statement, col) << " ";
            break;
          case SQLITE_TEXT:
            cout << " " << sqlite3_column_text(statement, col) << " ";
            break;
          case SQLITE_NULL:
            cout << " " << "NULL" << " ";
            break;
        }
      };
      cout << endl;
    } else if (ret == SQLITE_DONE) {
      cout << "done" << endl;
      break;
    } else {
      cout << "ret:" << ret << endl;
      break;
    }
  }

  sqlite3_finalize(statement);
  sqlite3_close(conn);

  return 0;
}


sqlite3_exec()是將sqlite3_prepare_v2(), sqlite3_step()和 sqlite3_finalize()包裝起來的API,其原型為
int sqlite3_exec(
  sqlite3*,                                  /* An open database */
  const char *sql,                           /* SQL to be evaluated */
  int (*callback)(void*,int,char**,char**),  /* Callback function */
  void *,                                    /* 1st argument to callback */
  char **errmsg                              /* Error msg written here */
);

  • 第二個參數要執行的SQL statement。
  • 第三個參數是callback function。每一個row都會呼叫該callback一次。
  • 第四個參數是要傳給callback function的data。
  • 第五個參數就是發生錯誤時,儲存錯誤資訊的指標。
因此上面的程式可以改寫為
#include <iostream>
#include <sqlite3.h>

using namespace std;

int callback(void *data, int column, char **value, char **name)
{
  int i;
  cout << (char *)data << endl;
  for (i = 0; i < column; i++) {
    cout << name[i] << ":" << (value[i] ?: "NULL") << endl << flush;
  }
  return 0;
}


int main(int argc, char** argv)
{
  sqlite3 *conn;
  int ret = 0;
  char *errmsg = NULL;
  char data[] = "brook";

  // This routine opens a connection to an SQLite database file
  //  and returns a database connection object.
  ret = sqlite3_open_v2("hello.db", &conn, SQLITE_OPEN_READONLY, NULL);
  if (ret != SQLITE_OK) {
     cout << sqlite3_errmsg(conn) << ".(" << ret << ")" << endl;
     exit(0);
  }

  ret = sqlite3_exec(conn, "select * from hello", callback, data, &errmsg);
  if (ret != SQLITE_OK) {
     cout << "We did not get any data. " << errmsg << endl;
     sqlite3_free(errmsg);
  }

  sqlite3_close(conn);

  return 0;
}






2012年7月28日 星期六

Introduction SQLite


大家比較耳熟能詳的database大多是client/server的架構居多,如:MySQL、PostgreSQL、MS-SQL和Oracle等等,而SQLite感覺比較像MS的Access, 程式本身就負責開啟資料庫並且直接操作,更多的資訊可以參考官網http://www.sqlite.org/

以下就個人在SQLite網站看到的介紹,寫下一些筆記。
About SQLite
SQLite不像許多SQL資料庫有分開的Server process。SQLite直接對資料庫檔案做存取,這些資料庫檔案室可以跨平台的, 意味著您可以直接在32/64和big-endian/little-endian複製這些資料庫檔案,都能正確地被SQLite所存取。

Appropriate Uses For SQLite/Situations Where Another RDBMS May Work Better SQLite因為直接對database做存取,所以多個client同時對某database做存取可能會有問題。 如果是高流量(High-volume Websites)或是高資料量(Very large datasets)都不適合SQLite,畢竟它是拿來給embedded用的。

總體而言,對於小資料庫的應用SQLite已經是非常好的選擇了。


基本操作
brook:~$ ls hello.db
ls: cannot access hello.db: No such file or directory
brook:~$ sqlite hello.db
Loading resources from /home/brook/.sqliterc
SQLite version 2.8.17
Enter ".help" for instructions
sqlite> .help
.databases             List names and files of attached databases
.dump ?TABLE? ...      Dump the database in a text format
.echo ON|OFF           Turn command echo on or off
.exit                  Exit this program
.explain ON|OFF        Turn output mode suitable for EXPLAIN on or off.
.header(s) ON|OFF      Turn display of headers on or off
.help                  Show this message
.indices TABLE         Show names of all indices on TABLE
.mode MODE             Set mode to one of "line(s)", "column(s)",
                       "insert", "list", or "html"
.mode insert TABLE     Generate SQL insert statements for TABLE
.nullvalue STRING      Print STRING instead of nothing for NULL data
.output FILENAME       Send output to FILENAME
.output stdout         Send output to the screen
.prompt MAIN CONTINUE  Replace the standard prompts
.quit                  Exit this program
.read FILENAME         Execute SQL in FILENAME
.schema ?TABLE?        Show the CREATE statements
.separator STRING      Change separator string for "list" mode
.show                  Show the current values for various settings
.tables ?PATTERN?      List names of tables matching a pattern
.timeout MS            Try opening locked tables for MS milliseconds
.width NUM NUM ...     Set column widths for "column" mode

sqlite> .tables
sqlite> create table hello (x integer PRIMARY KEY ASC, y);
sqlite> .tables
hello
sqlite> insert into hello (y) values('a');
sqlite> insert into hello (y) values(10);
sqlite> insert into hello (y) values(datetime('now'));
sqlite> select * from hello;
x           y
----------  ----------
1           a
2           10
3           2012-07-30
sqlite> .quit


我們也可以直接在command line上面執行sqlite
brook:~$ sqlite hello.db .dump
Loading resources from /home/brook/.sqliterc
BEGIN TRANSACTION;
create table hello (x integer PRIMARY KEY ASC, y);
INSERT INTO hello VALUES(1,'a');
INSERT INTO hello VALUES(2,10);
INSERT INTO hello VALUES(3,'2012-07-30 04:20:12');
COMMIT;
brook:~$ sqlite hello.db "select * from hello"
Loading resources from /home/brook/.sqliterc
x           y
----------  ----------
1           a
2           10
3           2012-07-30
brook:~$ cat ~/.sqliterc
.mode column
.header on
.nullvalue NULL

更多SQL語法請參考http://www.sqlite.org/lang.html





2012年6月30日 星期六

Pipe to a Subprocess


偶爾想偷懶的我會在code中使用system(),呼叫shell來執行一些東西,並且將執行完的結果丟回來程式中處理,這時候popen就很好用了,popen - pipe stream to or from a process。
popen允許開啟read或write pipe,不能夠同時開啟為read/write,read可以讓程式從popen中讀回來處理,而write可以將程式中的資料丟到popen處理。

popen(,"r")
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    char buf[128];
    float a, b, c, d;
    FILE *fp;
    if ((fp = popen("(df -h 2>/dev/null) | tail -n +2", "r")) == NULL) {
        fprintf(stderr, "popen() failed\n");
        return -1;
    }
    while (!feof(fp)) {
        fscanf(fp, "%*[^ ]%f%*[^1-9]%f%*[^1-9]%f%*[^1-9]%f%%%*[^\n]", &a, &b, &c, &d);
        printf("%f/%f/%f/%f\n", a, b, c, d);
    }
    return 0;
}


popen(,"w")
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
    char buf[] = "1 2 3 4 5";
    float a, b, c, d;
    FILE *fp;
    if ((fp = popen("xargs -n 1 > /tmp/xx", "w")) == NULL) {
        fprintf(stderr, "popen() failed\n");
        return -1;
    }
    fprintf(fp, "%s", buf);
    pclose(fp);
    return 0;
}


    參考資料:
  • The GNU C Library, Chapter 15.2




2012年5月26日 星期六

Design Patterns - Command pattern


Command pattern是一種將Action封裝起來,並提供一個general的interface作為呼叫時(invoke)的統一介面。

舉個生活例子來說明,想必您一定用過萬用遙控器,User可以設定遙控器上某個按鈕1的功能為電視的開關,所以當User按下按鈕1,就可以開關電視,接著User設定按鈕2為控制冷氣的開關,所以當User按下按鈕2,就可以開關冷氣,這樣簡單的運作就是一種Command Pattern。
電視開關是一種Action,被封裝成遙控器上面的按鈕,對User來說,這是一個General的interface,而且User在變更任和按鈕功能時,並不會影響遙控器上面的其他按鈕。

Command pattern有三個名詞需要解釋一下:
client:The client instantiates the command object and provides the information required to call the method at a later time.
invoker: The invoker decides when the method should be called.
receiver: The receiver is an instance of the class that contains the method's code.

client就可以想成"設定按鈕"的動作,invoker就是User了,而receiver就是電器了。

圖取自http://en.wikipedia.org/wiki/Command_pattern.


#include <iostream>

class Command {
public:
    virtual int exec(void *args) = 0;
};

// Receiver
class TV {
    bool turn_on_off;
public:
    void pwr_switch() {
        if (turn_on_off) {
            std::cout << "Turn off TV" << std::endl;
        } else {
            std::cout << "Turn on TV" << std::endl;
        }
        turn_on_off = !(turn_on_off);
    }
};

class CmdTvSwitch: public Command {
    TV *_tv;
public:
    CmdTvSwitch(TV *tv) {
        this->_tv = tv;
    }
    virtual int exec(void *args) {
        this->_tv->pwr_switch();
        return 0;
    }
};

int main(int argc, char *argv[])
{
    // Client
    TV tv;
    Command  *c;
    c = new CmdTvSwitch(&tv);
    c->exec(NULL);
    c->exec(NULL);
    return 0; 
}

就目前個人在使用這個Pattern上面的感觸就是"Command pattern是一種將Action封裝起來,並提供一個general的interface作為呼叫時(invoke)的統一介面。"比如,我在寫Message Queue會用到,提供新的指令供外界呼叫也會用到。
    參考資料:
  1. http://en.wikipedia.org/wiki/Command_pattern
  2. http://caterpillar.onlyfun.net/Gossip/DesignPattern/CommandPattern.htm




2012年5月19日 星期六

理財常用的Link



股匯市原物料期貨報價, http://www.stockq.org/
全球股市排行榜, http://www.stockq.org/market/
貨幣各國匯率歷史紀錄, http://fxtop.com/en/historates.php
金磚四國和台灣股市走勢圖, http://finance.yahoo.com
台灣地區銀行利率, http://www.taiwanrate.com/
台灣地區各網路銀行入口, http://www.easyatm.com.tw/playatm.html
文茜世界財經週報, http://www.youtube.com
57金錢爆, http://www.youtube.com
國際主要股市月報 , http://www.twse.com.tw/ch/statistics/statistics.php
基金績效,
台股上市公司本益比, http://www.twse.com.tw
台股上櫃公司本益比, http://www.otc.org.tw
基金定期定額試算, http://fund.scsb.com.tw
STOCK-AI提供各類經濟指標數據, https://stock-ai.com/
blog
法意聯盟, http://www.wretch.cc/blog/joejoejoe



持續更新中...



2012年4月7日 星期六

常用的regular expression


這篇會慢慢增加內容,所以我也會更新發布日期。
JavaScript
    var mac = /^\s*([\d[a-f]{2}:){5}[\d[a-f]{2}\s*$/i;
    var ip = /^((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))\.((\d)|(([1-9])\d)|(1\d\d)|(2(([0-4]\d)|5([0-5]))))$/;


C language
    char *mac =  "([[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}";
    char *ip = "^(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))\\.(([0-9])|(([1-9])[0-9])|(1[0-9][0-9])|(2(([0-4][0-9])|5([0-5]))))$";


    參考資料
  1. http://en.wikipedia.org/wiki/Regular_expression



2012年4月1日 星期日

頁面遮罩


我們常常可以在Web上看到,當有事件在背景執行時,Web會跳出半透明的視窗遮住後面的頁面,如:


這裡主要是利用JavaScript控制layer(Z-Index),以及style的opacity和style的display達成,layer控制element之間的stack,也就是MS office裡面常常提到的,"往上/下一層",style.opacity則是透明度,style.display控制顯示或隱藏,這些就可以達成頁面遮罩的功能了。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <script type="text/javascript">
//<![CDATA[
    function show() {
      document.getElementById("xie").style.display = "";
      document.getElementById("content1").style.display = "";
      document.getElementById("content1").innerHTML = "內容<br/><input onclick='hide()' type='button' value='確定'/>";
    }

    function hide() {
      document.getElementById("xie").style.display = "none";
      document.getElementById("content1").style.display = "none";
    }
  //]]>
  </script>
  <title>JavaScript Mask</title>
</head>

<body>
  <div style=
  "filter:alpha(opacity=50); -moz-opacity:0.5; opacity: 0.5; width: 100%; background-color: White; display: none; height: 100%; position: absolute; left: 0; top: 0;"
       id="xie"></div>

  <div style=
  "width: 100px; background-color: Red; display: none; height: 53px; position: absolute; left: 144px; top: 100px;"
       id="content1"></div>

  <input onclick="show()" type="button" value="顯示" />
</body>
</html>





2012年3月24日 星期六

動態產生HTML element -- document.createElement


我們可以利用document.createElement()動態的產生HTML的element,再利用Node.appendChild()插入某個Node底下,或利用Node.removeChild()自某個Node底下移除,更多資訊可以參考[1]。
<!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>auto table</title>
</head>

<body>
  <table id="brook" summary=""></table>
  <script type="text/javascript">
  //<![CDATA[
    function create_new_row_button(eT, msg) {
        var eR = document.createElement("tr");
        var eC = document.createElement("td");
        var eB = document.createElement("input");
        eB.type = "button"
        eB.value= msg;
        eB.style.width = "100px";

        eT.appendChild(eR);
        eR.appendChild(eC);
        eC.appendChild(eB);
    }
    var station = [
        {name: "CHT"},
        {name: "TW"},
    ];
    for (var i = 0; i < station.length; i++) {
        create_new_row_button(document.getElementById("brook"),
                              station[i].name);
    }
  //]]>
  </script>
</body>
</html>






2012年3月3日 星期六

glibc讀書心得 -- Ch2 Error Reporting


在glibc中會去偵錯並且回傳錯誤值,通常我們都應該去檢查這些回傳值,並且作error-handling,比如開檔 open()/fopen(),並非每次都會開成功,所以,open()/fopen()通常都會去檢查return value是不是有正確。

2.1 Checking for Errors
glibc的return value通常是-1、NULL或是EOF,這只能知道錯誤發生,至於發生何種錯誤,都是存在errno這個變數中,當您要使用errno必須include <errno.h>。這個header file也定義很多error code,都是以E為開頭,這些error code在Ch 2.2中有說明,用到再查就OK啦。

2.2 Error Codes
這個章節主要說明定義的error code,值得注意的是EAGAIN和EWOULDBLOCK的值一樣,所以不能放在同一個case switch中,其實我常用的error code大概只有他列的1/5不到吧,看看就好。

2.3 Error Messages 基本上我們都是希望report的錯誤是文字型態,而不是只有error code,所以glibc有提供一些轉換的function:
char * strerror(int errnum)[Function]
char * strerror_r(int errnum, char *buf, size t n)[Function]
void perror(const char *message)[Function]
char * program_invocation_name[Variable]
char * program_invocation_short_name[Variable]
void error (int status, int errnum, const char *format, . . . )[Function]
error_at_line (int status, int errnum, const char *fname,unsigned int lineno, const char *format, . . . )[Function]

char * strerror(int errnum)
將errnum轉成相對應的字串
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    printf("%s\n", strerror(EINTR));
    // print out "Interrupted system call"
    return 0;
}

char * strerror_r(int errnum, char *buf, size t n)
strerror的reentrant版本,mutli-thread就一定要用這個版本。
#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    char buf[32];
    printf("%s\n", strerror_r(EINTR, buf, sizeof(buf)));
    // print out "Interrupted system call"
    return 0;
}

void perror(const char *message)
效果和printf(stderr, "%s:%s", message, strerror(errno))相似,把error message寫到stderr去,接著冒號後面再接errno的message。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    perror("my_perror_msg1");
    // print out "my_perror_msg1: Success"
    errno = EINTR;
    perror("my_perror_msg2");
    // print out "my_perror_msg2: Interrupted system call"
    return 0;
}

char * program_invocation_name / char * program_invocation_short_name
該程式的名稱,和argv[0]的值一樣。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    printf("%s\n", argv[0]);
    // print out "./a.out"
    printf("%s\n", program_invocation_name);
    // print out "./a.out"
    printf("%s\n", program_invocation_short_name);
    // print out "a.out"
    return 0;
}


void error (int status, int errnum, const char *format, . . . )
status不為0時,就等同exit(status),後面的參數errnum和format用途類似perror(),當errnum為0時,就不會印後面的message,但是perror()會印Success。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    error(0, 0, "%s", "msg");
    error(0, EINTR, "%s", "msg");
    // print out "./a.out: msg: Interrupted system call"
    error(1, EINTR, "%s", "msg");
    // print out "./a.out: msg: Interrupted system call" and exit(1)
    return 0;
}


error_at_line(int status, int errnum, const char *fname, unsigned int lineno, const char *format, ...)
error_at_line()和error()很像,只是多了fname和lineno印出檔案名稱和行號。
#include <stdio.h>
#include <errno.h>  // EINTR
#include <string.h> // strerror()

int main(int argc, char *argv[])
{
    error_at_line(0, 0, __FILE__, __LINE__, "%s", "msg");
    // print out "./a.out:error_at_line.c:7: msg"
    error_at_line(0, EINTR, __FILE__, __LINE__, "%s", "msg");
    // print out "./a.out:error_at_line.c:8: msg: Interrupted system call"
    error_at_line(1, EINTR, __FILE__, __LINE__, "%s", "msg");
    // print out "./a.out:error_at_line.c:11: msg: Interrupted system call"
    return 0;
}



    參考資料:
  • The GNU C Library, Chapter 2



IE8無法下載檔案


話說有一天,我用Luci寫一段download動態產生的檔案時,發生部分的IE8下載有問題,都會產生無法下載的訊息,當然就是立刻請問google大神,尋得此文章[PHP]下載檔案時無法直接開啟文件的解法方法,雖然是用PHP寫,不過小改一下就可以在Luci上面如法炮製啦。

[PHP]下載檔案時無法直接開啟文件的解法方法
header("Content-Type: application/octetstream; name=$FILEname"); //for IE & Opera
header("Content-Type: application/octet-stream; name=$FILEname"); //for the rest
header("Content-Disposition: attachment; filename=$FILEname;");
header("Content-Transfer-Encoding: binary");
header("Cache-Control: cache, must-revalidate");
header("Pragma: public");
header("Expires: 0");


Luci不過就是改呼叫luci.http.header()。
[Luci]下載檔案時無法直接開啟文件的解法方法
luci.http.header("Content-Type", "application/octetstream; name=" .. fname); //for IE & Opera
luci.http.header("Content-Type", "application/octet-stream; name=" .. fname); //for the rest
luci.http.header("Content-Disposition", "attachment; filename=" .. fname);
luci.http.header("Content-Transfer-Encoding", "binary");
luci.http.header("Cache-Control", "cache, must-revalidate");
luci.http.header("Pragma", "public");
luci.http.header("Expires", "0");




2012年2月26日 星期日

glibc讀書心得 -- Ch1 Introduction


1.1 Getting Started
C語言並沒有內建一些常用的操作,如input/output、memory management等等,這些通通被定義在Standard Library中(glibc)。

1.2 Standards and Portability
glibc參考的C Library標準有ISO C(American National Standard X3.159-1989—“ANSI C” and later by the International Standardization Organization (ISO): ISO/IEC 9899:1990, “Programming languages—C”.)和POSIX兩個。以及參考System V和Berkeley UNIX兩個實做而成。

1.3 Using the Library
Library在C主要由兩個部份組成:
  1. header files that define types and macros and declare variables and functions
  2. the actual library or archive that contains the definitions of the variables and functions.
為了能使用glibc,你必須將所需的header file,用#include這個preprocessor directive將其引入,嚴格來說,您可以不必include這些header file,只要您可以正確的宣告相關的變數、巨集等等,不過為了效率和能正確的宣告相關變數,我們都會採用header file的形式,將其引入(include)。至於Macro的部份就參考我之前的C preprocessor的文章GCC - C Preprocessor應該會更清楚。
關於Reserved Names個人覺得還蠻重要的,就直接貼出來了:
  1. Names beginning with a capital ‘E’ followed a digit or uppercase letter may be used for additional error code names. See Chapter 2 [Error Reporting], page 13.
  2. Names that begin with either ‘is’ or ‘to’ followed by a lowercase letter may be used for additional character testing and conversion functions. See Chapter 4 [Character Handling], page 65.
  3. Names that begin with ‘LC_’ followed by an uppercase letter may be used for additional macros specifying locale attributes. See Chapter 7 [Locales and Internationalization], page 150.
  4. Names of all existing mathematics functions (see Chapter 19 [Mathematics], page 466) suffixed with ‘f’ or ‘l’ are reserved for corresponding functions that operate on float and long double arguments, respectively.
  5. Names that begin with ‘SIG’ followed by an uppercase letter are reserved for additional signal names. See Section 24.2 [Standard Signals], page 592.
  6. Names that begin with ‘SIG_’ followed by an uppercase letter are reserved for additional signal actions. See Section 24.3.1 [Basic Signal Handling], page 600.
  7. Names beginning with ‘str’, ‘mem’, or ‘wcs’ followed by a lowercase letter are reserved for additional string and array functions. See Chapter 5 [String and Array Utilities], page 73.
  8. Names that end with ‘_t’ are reserved for additional type names.


    參考資料:
  1. The GNU C Library, Chapter 1




2012年1月29日 星期日

JSON-C


JSON-C是一套用C寫的JSON format的parser和generator,可以將JSON字串正確parse,並且存成json_object進行操作(新增/刪除/修改),也可以將json_object轉成JSON字串,個人還蠻推薦的。

官方網站:JSON-C

安裝步驟
brook@vista:~/src$ git clone https://github.com/json-c/json-c.git json-c
Cloning into json-c...
remote: Counting objects: 448, done.
remote: Compressing objects: 100% (169/169), done.
remote: Total 448 (delta 315), reused 405 (delta 274)
Receiving objects: 100% (448/448), 125.68 KiB | 76 KiB/s, done.
Resolving deltas: 100% (315/315), done.
brook@vista:~/src$ cd json-c/
brook@vista:~/src/json-c$ ./autogen.sh
autoreconf: Entering directory `.'
autoreconf: configure.in: not using Gettext
autoreconf: running: aclocal 
autoreconf: configure.in: tracing
autoreconf: running: libtoolize --install --copy
libtoolize: Consider adding `AC_CONFIG_MACRO_DIR([m4])' to configure.in and
libtoolize: rerunning libtoolize, to keep the correct libtool macros in-tree.
libtoolize: Consider adding `-I m4' to ACLOCAL_AMFLAGS in Makefile.am.
autoreconf: running: /usr/bin/autoconf
autoreconf: running: /usr/bin/autoheader
autoreconf: running: automake --add-missing --copy --no-force
autoreconf: Leaving directory `.'
brook@vista:~/src/json-c$ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether build environment is sane... yes
checking for a thread-safe mkdir -p... /bin/mkdir -p
...
brook@vista:~/src/json-c$ make
make  all-am
make[1]: Entering directory `/home/brook/src/json-c'
/bin/bash ./libtool --tag=CC   --mode=compile gcc -DHAVE_CONFIG_H -I.    -Wall -Wwrite-strings -Werror -std=gnu99 -D_GNU_SOURCE -D_REENTRANT -g -O2 -MT arraylist.lo -MD -MP -MF .deps/arraylist.Tpo -c -o arraylist.lo arraylist.c
...
brook@vista:~/src/json-c$ make check
make  test1 test2 test4 test_parse_int64 test_null test_cast
make[1]: Entering directory `/home/brook/src/json-c'
gcc -DHAVE_CONFIG_H -I.    -Wall -Wwrite-strings -Werror -std=gnu99 -D_GNU_SOURCE -D_REENTRANT -g -O2 -MT test1.o -MD -MP -MF .deps/test1.Tpo -c -o test1.o test1.c
test1.c: In function ‘sort_fn’:
test1.c:14:8: error: assignment discards ‘const’ qualifier from pointer target type [-Werror]
test1.c:15:8: error: assignment discards ‘const’ qualifier from pointer target type [-Werror]
cc1: all warnings being treated as errors

make[1]: *** [test1.o] Error 1
make[1]: Leaving directory `/home/brook/src/json-c'
make: *** [check-am] Error 2
brook@vista:~/src/json-c$ sed -i 's/-Werror //' Makefile
brook@vista:~/src/json-c$ make check
make  test1 test2 test4 test_parse_int64 test_null test_cast
make[1]: Entering directory `/home/brook/src/json-c'
...
brook@vista:~/src/json-c$ ./test1
my_string= 
my_string.to_string()="\t"
my_string=\
my_string.to_string()="\\"
my_string=foo
my_string.to_string()="foo"
my_int=9
my_int.to_string()=9
my_array=
 [0]=1
 [1]=2
 [2]=3
 [3]=null
 [4]=5
...


Parser
可以利用json_tokener_parse()直接將字串轉成json_object,或是利用json_object_from_file()直接將檔案轉成json_object。
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>

#include "json.h"


int main(int argc, char **argv)
{
  json_object *new_obj;

  MC_SET_DEBUG(1);

  new_obj = json_tokener_parse("/* more difficult test case */ { \"glossary\": { \"title\": \"example glossary\", \"GlossDiv\": { \"title\": \"S\", \"GlossList\": [ { \"ID\": \"SGML\", \"SortAs\": \"SGML\", \"GlossTerm\": \"Standard Generalized Markup Language\", \"Acronym\": \"SGML\", \"Abbrev\": \"ISO 8879:1986\", \"GlossDef\": \"A meta-markup language, used to create markup languages such as DocBook.\", \"GlossSeeAlso\": [\"GML\", \"XML\", \"markup\"] } ] } } }");
  printf("new_obj.to_string()=%s\n", json_object_to_json_string(new_obj));
  json_object_put(new_obj);

  return 0;
}


Generator基本上就是用json_object_to_json_string()將json_object轉成字串。

更多的範例可以參考source code裡面的test1.c。