# SQLite C 教程
> 原文: [http://zetcode.com/db/sqlitec/](http://zetcode.com/db/sqlitec/)
這是 SQLite 數據庫的 C 編程教程。 它涵蓋了使用 C 語言進行 SQLite 編程的基礎。 您可能還想查看 ZetCode 上的 [SQLite 教程](/db/sqlite/), [MySQL C 教程](/db/mysqlc/)或 [PostgreSQL C 教程](/db/postgresqlc/)。
## SQLite 數據庫
_SQLite_ 是嵌入式關系數據庫引擎。 它的開發者稱其為自包含,無服務器,零配置和事務型 SQL 數據庫引擎。 它目前非常流行,當今全球使用著數億冊。 SQLite 在 Solaris 10,Mac OS,Android 或 iPhone 中使用。 Qt4 庫內置了對 SQLite 以及 Python 和 PHP 的支持。 許多流行的應用內部都使用 SQLite,例如 Firefox,Google Chrome 或 Amarok。
## sqlite3 工具
_sqlite3_ 工具是 SQLite 庫的基于終端的前端。 它以交互方式求值查詢,并以多種格式顯示結果。 它也可以在腳本中使用。 它具有自己的元命令集,包括`.tables`,`.load`,`.databases`或`.dump`。 要獲取所有指令的列表,我們鍵入`.help`命令。
現在,我們將使用`sqlite3`工具創建一個新數據庫。
```c
$ sqlite3 test.db
SQLite version 3.8.2 2013-12-06 14:53:30
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
```
我們為`sqlite3 tool`提供了一個參數; `test.db`是數據庫名稱。 這是我們磁盤上的文件。 如果存在,則將其打開。 如果不是,則創建它。
```c
sqlite> .tables
sqlite> .exit
$ ls
test.db
```
`.tables`命令提供`test.db`數據庫中的表的列表。 當前沒有表。 `.exit`命令終止`sqlite3`命令行工具的交互式會話。 `ls` Unix 命令顯示當前工作目錄的內容。 我們可以看到`test.db`文件。 所有數據將存儲在該單個文件中。
## C99
本教程使用 C99。 對于 GNU C 編譯器,我們需要使用`-std=c99`選項。 對于 Windows 用戶,強烈建議使用 Pelles C IDE。 (MSVC 不支持 C99。)
```c
int rc = sqlite3_open("test.db", &db);
```
在 C99 中,我們可以將聲明與代碼混合使用。 在較早的 C 程序中,我們需要將這一行分成兩行。
## SQLite 版本
在第一個代碼示例中,我們將獲得 SQLite 數據庫的版本。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
printf("%s\n", sqlite3_libversion());
return 0;
}
```
`sqlite3_libversion()`函數返回指示 SQLite 庫的字符串。
```c
#include <sqlite3.h>
```
該頭文件定義了 SQLite 庫提供給客戶端程序的接口。 它包含定義,函數原型和注釋。 它是 SQLite API 的權威來源。
```c
$ gcc -o version version.c -lsqlite3 -std=c99
```
我們使用 GNU C 編譯器編譯程序。
```c
$ ./version
3.8.2
```
這是示例的輸出。
在第二個示例中,我們再次獲得 SQLite 數據庫的版本。 這次我們將使用 SQL 查詢。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
sqlite3_stmt *res;
int rc = sqlite3_open(":memory:", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_prepare_v2(db, "SELECT SQLITE_VERSION()", -1, &res, 0);
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_step(res);
if (rc == SQLITE_ROW) {
printf("%s\n", sqlite3_column_text(res, 0));
}
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}
```
`SQLITE_VERSION()`查詢用于獲取 SQLite 庫的版本。
```c
sqlite3 *db;
```
`sqlite3`結構定義數據庫句柄。 每個打開的 SQLite 數據庫均由數據庫句柄表示。
```c
sqlite3_stmt *res;
```
`sqlite3_stmt`結構表示單個 SQL 語句。
```c
int rc = sqlite3_open(":memory:", &db);
```
`sqlite3_open()`函數打開一個新的數據庫連接。 它的參數是數據庫名稱和數據庫句柄。 `:memory:`是一個特殊的數據庫名稱,使用它可以打開內存數據庫。 該函數的返回代碼指示數據庫是否已成功打開。 成功建立連接后,將返回`SQLITE_OK`。
```c
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
```
如果返回代碼指示錯誤,我們將消息打印到控制臺,關閉數據庫句柄,然后終止程序。 `sqlite3_errmsg()`函數返回錯誤的描述。 無論打開時是否發生錯誤,都應通過將其傳遞給`sqlite3_close()`函數來釋放與數據庫連接句柄關聯的資源。
```c
rc = sqlite3_prepare_v2(db, "SELECT SQLITE_VERSION()", -1, &res, 0);
```
在執行 SQL 語句之前,必須先使用`sqlite3_prepare*`函數之一將其編譯為字節碼。 (不推薦使用`sqlite3_prepare()`函數。)
`sqlite3_prepare_v2()`函數具有五個參數。 第一個參數是從`sqlite3_open()`函數獲得的數據庫句柄。 第二個參數是要編譯的 SQL 語句。 第三個參數是 SQL 語句的最大長度,以字節為單位。 傳遞 -1 將導致 SQL 字符串被讀取到第一個零終止符,即第一個零終止符。 根據文檔,通過傳遞所提供的 SQL 字符串的確切字節數,可以獲得一些小的性能優勢。 第四個參數是語句句柄。 如果`sqlite3_prepare_v2()`成功運行,它將指向預編譯的語句。 最后一個參數是指向 SQL 語句未使用部分的指針。 只編譯 SQL 字符串的第一條語句,因此該參數指向未編譯的內容。 我們傳遞 0,因為參數對我們而言并不重要。
成功時,`sqlite3_prepare_v2()`返回`SQLITE_OK`; 否則返回錯誤代碼。
```c
if (rc != SQLITE_OK) {
fprintf(stderr, "Failed to fetch data: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
```
這是`sqlite3_prepare_v2()`函數調用的錯誤處理代碼。
```c
rc = sqlite3_step(res);
```
`sqlite3_step()`運行 SQL 語句。 `SQLITE_ROW`返回碼表示還有另一行準備就緒。 我們的 SQL 語句僅返回一行數據,因此,我們只調用一次此函數。
```c
sqlite3_finalize(res);
```
`sqlite3_finalize()`函數破壞預備語句對象。
```c
sqlite3_close(db);
```
`sqlite3_close()`函數關閉數據庫連接。
## 插入數據
我們創建一個`Cars`表并在其中插入幾行。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "DROP TABLE IF EXISTS Cars;"
"CREATE TABLE Cars(Id INT, Name TEXT, Price INT);"
"INSERT INTO Cars VALUES(1, 'Audi', 52642);"
"INSERT INTO Cars VALUES(2, 'Mercedes', 57127);"
"INSERT INTO Cars VALUES(3, 'Skoda', 9000);"
"INSERT INTO Cars VALUES(4, 'Volvo', 29000);"
"INSERT INTO Cars VALUES(5, 'Bentley', 350000);"
"INSERT INTO Cars VALUES(6, 'Citroen', 21000);"
"INSERT INTO Cars VALUES(7, 'Hummer', 41400);"
"INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
```
我們連接到`test.db`數據庫,創建`Cars`表,然后在創建的表中插入 8 行。
```c
char *err_msg = 0;
```
如果發生錯誤,該指針將指向創建的錯誤消息。
```c
int rc = sqlite3_open("test.db", &db);
```
與`test.db`數據庫的連接已創建。
```c
char *sql = "DROP TABLE IF EXISTS Cars;"
"CREATE TABLE Cars(Id INT, Name TEXT, Price INT);"
"INSERT INTO Cars VALUES(1, 'Audi', 52642);"
"INSERT INTO Cars VALUES(2, 'Mercedes', 57127);"
"INSERT INTO Cars VALUES(3, 'Skoda', 9000);"
"INSERT INTO Cars VALUES(4, 'Volvo', 29000);"
"INSERT INTO Cars VALUES(5, 'Bentley', 350000);"
"INSERT INTO Cars VALUES(6, 'Citroen', 21000);"
"INSERT INTO Cars VALUES(7, 'Hummer', 41400);"
"INSERT INTO Cars VALUES(8, 'Volkswagen', 21600);";
```
這些 SQL 語句創建一個`Cars`表并用數據填充它。 語句必須用分號分隔。
```c
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
```
`sqlite3_exec()`函數是`sqlite3_prepare_v2()`,`sqlite3_step()`和`sqlite3_finalize()`的便利包裝,它允許應用運行多個 SQL 語句而無需使用大量 C 代碼。
該函數的第三個參數是為從求值的 SQL 語句中出來的每個結果行調用的回調函數。 第四個參數是回調函數的第一個參數。 如果不需要它們,可以將 0 傳遞給這些參數。
如果發生錯誤,則最后一個參數指向分配的錯誤消息。
```c
sqlite3_free(err_msg);
```
必須使用`sqlite3_free()`函數調用釋放分配的消息字符串。
```c
sqlite> .mode column
sqlite> .headers on
```
我們使用`sqlite3`工具驗證寫入的數據。 首先,我們修改數據在控制臺中的顯示方式。 我們使用列模式并打開標題。
```c
sqlite> SELECT * FROM Cars;
Id Name Price
---------- ---------- ----------
1 Audi 52642
2 Mercedes 57127
3 Skoda 9000
4 Volvo 29000
5 Bentley 350000
6 Citroen 21000
7 Hummer 41400
8 Volkswagen 21600
```
這是我們已寫入`Cars`表的數據。
## 最后插入的行 ID
有時,我們需要確定最后插入的行的 ID。 為此,我們具有`sqlite3_last_insert_rowid()`函數。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open(":memory:", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friends(Name) VALUES ('Roger');"
"INSERT INTO Friends(Name) VALUES ('Robert');";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "Failed to create table\n");
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
} else {
fprintf(stdout, "Table Friends created successfully\n");
}
int last_id = sqlite3_last_insert_rowid(db);
printf("The last Id of the inserted row is %d\n", last_id);
sqlite3_close(db);
return 0;
}
```
在內存中創建一個`Friends`表。 其`Id`列會自動增加。
```c
char *sql = "CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friends(Name) VALUES ('Roger');"
"INSERT INTO Friends(Name) VALUES ('Robert');";
```
在 SQLite 中,`INTEGER PRIMARY KEY`列自動增加。 還有一個`AUTOINCREMENT`關鍵字。 當在`INTEGER PRIMARY KEY AUTOINCREMENT`中應用時,會使用稍微不同的 ID 創建算法。
使用自動遞增的列時,除了自動遞增的列(省略了該列)外,我們需要明確聲明列名。
```c
int last_id = sqlite3_last_insert_rowid(db);
printf("The last Id of the inserted row is %d\n", last_id);
```
`sqlite3_last_insert_rowid()`返回表中最近一次成功插入的行 ID。
```c
$ ./last_row_id
Table Friends created successfully
The last Id of the inserted row is 5
```
我們看到了程序的輸出。
## 檢索數據
我們已經將一些數據插入`test.db`數據庫。 在下面的示例中,我們從數據庫中檢索數據。
```c
#include <sqlite3.h>
#include <stdio.h>
int callback(void *, int, char **, char **);
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n",
sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "SELECT * FROM Cars";
rc = sqlite3_exec(db, sql, callback, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "Failed to select data\n");
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
int callback(void *NotUsed, int argc, char **argv,
char **azColName) {
NotUsed = 0;
for (int i = 0; i < argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
```
我們使用`SELECT * FROM Cars` SQL 語句從`Cars`表中檢索所有行。
```c
int callback(void *, int, char **, char **);
```
這是與`sqlite3_exec()`函數結合使用的回調函數的函數原型。
```c
int rc = sqlite3_open("test.db", &db);
```
我們連接到`test.db`數據庫。
```c
char *sql = "SELECT * FROM Cars";
```
在這里,我們定義 SQL 語句以從`Cars`表中選擇所有數據。
```c
rc = sqlite3_exec(db, sql, callback, 0, &err_msg);
```
`sqlite3_exec()`函數對 SQL 語句進行賦值。 對于從求值的 SQL 語句出來的每個結果行,都會調用其回調函數。
```c
int callback(void *NotUsed, int argc, char **argv,
char **azColName) {
NotUsed = 0;
for (int i = 0; i < argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
```
回調函數的第一個參數是`sqlite3_exec()`的第 4 個參數中提供的數據; 它通常不被使用。 第二個參數是結果中的列數。 第三個參數是表示行中字段的字符串數組。 最后一個參數是代表列名的字符串數組。
在函數主體中,我們遍歷所有列并打印其名稱和內容。
```c
$ ./select_all
Id = 1
Name = Audi
Price = 52642
Id = 2
Name = Mercedes
Price = 57127
Id = 3
Name = Skoda
Price = 9000
...
```
這是示例的部分輸出。
## 參數化查詢
現在我們將提到參數化查詢。 參數化查詢(也稱為預準備語句)可提高安全性和性能。 當使用參數化查詢時,我們使用占位符,而不是直接將值寫入語句。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
sqlite3_stmt *res;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "SELECT Id, Name FROM Cars WHERE Id = ?";
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
if (rc == SQLITE_OK) {
sqlite3_bind_int(res, 1, 3);
} else {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
}
int step = sqlite3_step(res);
if (step == SQLITE_ROW) {
printf("%s: ", sqlite3_column_text(res, 0));
printf("%s\n", sqlite3_column_text(res, 1));
}
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}
```
在該示例中,問號(?)用作占位符,稍后將其替換為實際值。
```c
char *sql = "SELECT Id, Name FROM Cars WHERE Id = ?";
```
問號用于為 SQL 查詢提供 ID。
```c
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
```
`sqlite3_prepare_v2()`函數編譯 SQL 查詢。
```c
sqlite3_bind_int(res, 1, 3);
```
`sqlite3_bind_int()`將整數值綁定到預備語句。 占位符將替換為整數 3。該函數的第二個參數是要設置的 SQL 參數的索引,第三個參數是要綁定到該參數的值。
```c
int step = sqlite3_step(res);
```
`sqlite3_step()`函數求值 SQL 語句。
```c
if (step == SQLITE_ROW) {
printf("%s: ", sqlite3_column_text(res, 0));
printf("%s\n", sqlite3_column_text(res, 1));
}
```
如果有可用的數據行,則可以使用`sqlite3_column_text()`函數獲得兩列的值。
```c
$ ./parameterized
3: Skoda
```
該示例返回 ID 和汽車的名稱。
第二個示例使用帶有命名占位符的參數化語句。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
sqlite3_stmt *res;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "SELECT Id, Name FROM Cars WHERE Id = @id";
rc = sqlite3_prepare_v2(db, sql, -1, &res, 0);
if (rc == SQLITE_OK) {
int idx = sqlite3_bind_parameter_index(res, "@id");
int value = 4;
sqlite3_bind_int(res, idx, value);
} else {
fprintf(stderr, "Failed to execute statement: %s\n", sqlite3_errmsg(db));
}
int step = sqlite3_step(res);
if (step == SQLITE_ROW) {
printf("%s: ", sqlite3_column_text(res, 0));
printf("%s\n", sqlite3_column_text(res, 1));
}
sqlite3_finalize(res);
sqlite3_close(db);
return 0;
}
```
我們使用命名的占位符選擇汽車的名稱和價格。
```c
char *sql = "SELECT Id, Name FROM Cars WHERE Id = @id";
```
命名的占位符以冒號(:)或符號(@)字符為前綴。
```c
int idx = sqlite3_bind_parameter_index(res, "@id");
```
`sqlite3_bind_parameter_index()`函數返回給定名稱的 SQL 參數的索引。
## 插入圖像
在本節中,我們將圖像插入到 SQLite 數據庫中。 請注意,有些人反對將圖像放入數據庫。 在這里,我們只展示如何做。 我們不討論是否將圖像保存在數據庫中的技術問題。
```c
sqlite> CREATE TABLE Images(Id INTEGER PRIMARY KEY, Data BLOB);
```
對于此示例,我們創建一個名為`Images`的新表。 對于圖像,我們使用`BLOB`數據類型,表示二進制大型對象。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(int argc, char **argv) {
FILE *fp = fopen("woman.jpg", "rb");
if (fp == NULL) {
fprintf(stderr, "Cannot open image file\n");
return 1;
}
fseek(fp, 0, SEEK_END);
if (ferror(fp)) {
fprintf(stderr, "fseek() failed\n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
return 1;
}
int flen = ftell(fp);
if (flen == -1) {
perror("error occurred");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
return 1;
}
fseek(fp, 0, SEEK_SET);
if (ferror(fp)) {
fprintf(stderr, "fseek() failed\n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
return 1;
}
char data[flen+1];
int size = fread(data, 1, flen, fp);
if (ferror(fp)) {
fprintf(stderr, "fread() failed\n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
return 1;
}
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
sqlite3_stmt *pStmt;
char *sql = "INSERT INTO Images(Data) VALUES(?)";
rc = sqlite3_prepare(db, sql, -1, &pStmt, 0);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot prepare statement: %s\n", sqlite3_errmsg(db));
return 1;
}
sqlite3_bind_blob(pStmt, 1, data, size, SQLITE_STATIC);
rc = sqlite3_step(pStmt);
if (rc != SQLITE_DONE) {
printf("execution failed: %s", sqlite3_errmsg(db));
}
sqlite3_finalize(pStmt);
sqlite3_close(db);
return 0;
}
```
在此程序中,我們從當前工作目錄中讀取圖像,并將其寫入 SQLite `test.db`數據庫的`Images`表中。
```c
FILE *fp = fopen("woman.jpg", "rb");
if (fp == NULL) {
fprintf(stderr, "Cannot open image file\n");
return 1;
}
```
我們從文件系統讀取二進制數據。 我們有一個名為`woman.jpg`的 JPG 圖像。 `fopen()`函數打開指定的文件以供讀取。 如果操作失敗,它將返回一個指向`FILE`對象的指針或`NULL`。
```c
fseek(fp, 0, SEEK_END);
if (ferror(fp)) {
fprintf(stderr, "fseek() failed\n");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
return 1;
}
```
我們使用`fseek()`函數將文件指針移到文件末尾。 我們需要確定圖像的大小。 如果發生錯誤,則設置錯誤指示器。 我們使用`fseek()`函數檢查指示器。 如果發生錯誤,將關閉打開的文件處理器。
```c
int flen = ftell(fp);
if (flen == -1) {
perror("error occurred");
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
return 1;
}
```
對于二進制流,`ftell()`函數返回文件開頭的字節數,例如圖像文件的大小。 發生錯誤時,函數將返回 -1 并設置`errno`。 `perror()`函數將`errno`的值解釋為錯誤消息,并將其打印到標準錯誤輸出流。
```c
char data[flen+1];
```
該數組將存儲圖像數據。
```c
int size = fread(data, 1, flen, fp);
```
`fread()`函數從文件指針讀取數據并將其存儲在數據數組中。 該函數返回成功讀取的元素數。
```c
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
```
讀取數據后,我們可以關閉文件處理器。
```c
char *sql = "INSERT INTO Images(Data) VALUES(?)";
```
該 SQL 語句用于將映像插入數據庫。
```c
rc = sqlite3_prepare(db, sql, -1, &pStmt, 0);
```
SQL 語句已編譯。
```c
sqlite3_bind_blob(pStmt, 1, data, size, SQLITE_STATIC);
```
`sqlite3_bind_blob()`函數將二進制數據綁定到已編譯的語句。 `SQLITE_STATIC`參數表示指向內容信息的指針是靜態的,不需要釋放。
```c
rc = sqlite3_step(pStmt);
```
執行該語句,并將圖像寫入表中。
## 讀取圖像
在本節中,我們將執行相反的操作。 我們將從數據庫表中讀取圖像。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
FILE *fp = fopen("woman2.jpg", "wb");
if (fp == NULL) {
fprintf(stderr, "Cannot open image file\n");
return 1;
}
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "SELECT Data FROM Images WHERE Id = 1";
sqlite3_stmt *pStmt;
rc = sqlite3_prepare_v2(db, sql, -1, &pStmt, 0);
if (rc != SQLITE_OK ) {
fprintf(stderr, "Failed to prepare statement\n");
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
rc = sqlite3_step(pStmt);
int bytes = 0;
if (rc == SQLITE_ROW) {
bytes = sqlite3_column_bytes(pStmt, 0);
}
fwrite(sqlite3_column_blob(pStmt, 0), bytes, 1, fp);
if (ferror(fp)) {
fprintf(stderr, "fwrite() failed\n");
return 1;
}
int r = fclose(fp);
if (r == EOF) {
fprintf(stderr, "Cannot close file handler\n");
}
rc = sqlite3_finalize(pStmt);
sqlite3_close(db);
return 0;
}
```
我們從`Images`表中讀取圖像數據,并將其寫入另一個文件`woman2.jpg`中。
```c
FILE *fp = fopen("woman2.jpg", "wb");
if (fp == NULL) {
fprintf(stderr, "Cannot open image file\n");
return 1;
}
```
我們以寫入模式打開一個二進制文件。 來自數據庫的數據被寫入文件。
```c
char *sql = "SELECT Data FROM Images WHERE Id = 1";
```
該 SQL 語句從`Images`表中選擇數據。 我們從第一行獲取二進制數據。
```c
if (rc == SQLITE_ROW) {
bytes = sqlite3_column_bytes(pStmt, 0);
}
```
`sqlite3_column_bytes()`函數返回`BLOB`中的字節數。
```c
fwrite(sqlite3_column_blob(pStmt, 0), bytes, 1, fp);
```
使用`fwrite()`函數將二進制數據寫入文件。 `sqlite3_column_blob()`函數返回指向所選二進制數據的指針。
```c
if (ferror(fp)) {
fprintf(stderr, "fwrite() failed\n");
return 1;
}
```
`ferror()`函數檢查與流相關的錯誤指示符是否已設置。
## 元數據
元數據是有關數據庫中數據的信息。 SQLite 中的元數據包含有關表和列的信息,我們在其中存儲數據。 受 SQL 語句影響的行數是元數據。 結果集中返回的行數和列數也屬于元數據。
可以使用`PRAGMA`命令獲取 SQLite 中的元數據。 SQLite 對象可能具有屬性,即元數據。 最后,我們還可以通過查詢 SQLite 系統`sqlite_master`表來獲取特定的元數據。
```c
#include <sqlite3.h>
#include <stdio.h>
int callback(void *, int, char **, char **);
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n",
sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "PRAGMA table_info(Cars)";
rc = sqlite3_exec(db, sql, callback, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "Failed to select data\n");
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
int callback(void *NotUsed, int argc, char **argv,
char **azColName) {
NotUsed = 0;
for (int i = 0; i < argc; i++) {
printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
printf("\n");
return 0;
}
```
在此示例中,我們發出`PRAGMA table_info(tableName)`命令,以獲取有關`Cars`表的一些元數據信息。
```c
char *sql = "PRAGMA table_info(Cars)";
```
`PRAGMA table_info(tableName)`命令為`Cars`表中的每一列返回一行。 結果集中的列包括列順序號,列名稱,數據類型,該列是否可以為`NULL`以及該列的默認值。
```c
$ ./column_names
cid = 0
name = Id
type = INT
notnull = 0
dflt_value = NULL
pk = 0
...
```
這是示例的輸出。
在與元數據有關的下一個示例中,我們將列出`test.db`數據庫中的所有表。
```c
#include <sqlite3.h>
#include <stdio.h>
int callback(void *, int, char **, char **);
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n",
sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "SELECT name FROM sqlite_master WHERE type='table'";
rc = sqlite3_exec(db, sql, callback, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "Failed to select data\n");
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
int callback(void *NotUsed, int argc, char **argv,
char **azColName) {
NotUsed = 0;
for (int i = 0; i < argc; i++) {
printf("%s\n", argv[i] ? argv[i] : "NULL");
}
return 0;
}
```
該代碼示例將當前數據庫中的所有可用表打印到終端。
```c
char *sql = "SELECT name FROM sqlite_master WHERE type='table'";
```
表名存儲在系統`sqlite_master`表中。
```c
$ ./list_tables
Cars
Images
```
這是一個示例輸出。
## 事務
事務是針對一個或多個數據庫中數據的數據庫操作的基本單位。 事務中所有 SQL 語句的影響可以全部提交給數據庫,也可以全部回滾。
在 SQLite 中,除`SELECT`以外的任何命令都將啟動隱式事務。 同樣,在事務中,諸如`CREATE TABLE` ...,`VACUUM`和`PRAGMA`之類的命令將在執行之前提交先前的更改。
手動事務以`BEGIN TRANSACTION`語句開始,并以`COMMIT`或`ROLLBACK`語句結束。
SQLite 支持三種非標準事務級別:`DEFERRED`,`IMMEDIATE`和`EXCLUSIVE`。
### 自動提交
默認情況下,SQLite 版本 3 在自動提交模式下運行。 在自動提交模式下,對數據庫的所有更改將在與當前數據庫連接關聯的所有操作完成后立即提交。 自動提交模式由`BEGIN`語句禁用,并由`COMMIT`或`ROLLBACK`重新啟用。
```c
#include <sqlite3.h>
#include <stdio.h>
int main() {
sqlite3 *db;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n",
sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
printf("Autocommit: %d\n", sqlite3_get_autocommit(db));
sqlite3_close(db);
return 0;
}
```
本示例檢查數據庫是否處于自動提交模式。
```c
printf("Autocommit: %d\n", sqlite3_get_autocommit(db));
```
如果數據庫未處于自動提交模式,則`sqlite3_get_autocommit()`函數返回零。 如果處于自動提交模式,它將返回非零值。
```c
$ ./get_ac_mode
Autocommit: 1
```
該示例確認默認情況下 SQLite 處于自動提交模式。
下一個示例進一步闡明了自動提交模式。 在自動提交模式下,每個非`SELECT`語句都是一個立即提交的小事務。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "DROP TABLE IF EXISTS Friends;"
"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friend(Name) VALUES ('Robert');";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
```
我們創建`Friends`表,并嘗試用數據填充它。
```c
char *sql = "DROP TABLE IF EXISTS Friends;"
"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friend(Name) VALUES ('Robert');";
```
最后一條 SQL 語句有一個錯誤; 沒有朋友表。
```c
$ ./autocommit
SQL error: no such table: Friend
$ sqlite3 test.db
sqlite> .tables
Cars Friends Images
sqlite> SELECT * FROM Friends;
1|Tom
2|Rebecca
3|Jim
```
創建表并插入三行。
### 事務
在下一個示例中,我們將一些 SQL 語句放入事務中。
```c
#include <sqlite3.h>
#include <stdio.h>
int main(void) {
sqlite3 *db;
char *err_msg = 0;
int rc = sqlite3_open("test.db", &db);
if (rc != SQLITE_OK) {
fprintf(stderr, "Cannot open database: %s\n", sqlite3_errmsg(db));
sqlite3_close(db);
return 1;
}
char *sql = "DROP TABLE IF EXISTS Friends;"
"BEGIN TRANSACTION;"
"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friend(Name) VALUES ('Robert');"
"COMMIT;";
rc = sqlite3_exec(db, sql, 0, 0, &err_msg);
if (rc != SQLITE_OK ) {
fprintf(stderr, "SQL error: %s\n", err_msg);
sqlite3_free(err_msg);
sqlite3_close(db);
return 1;
}
sqlite3_close(db);
return 0;
}
```
我們繼續使用`Friends`表。
```c
char *sql = "DROP TABLE IF EXISTS Friends;"
"BEGIN TRANSACTION;"
"CREATE TABLE Friends(Id INTEGER PRIMARY KEY, Name TEXT);"
"INSERT INTO Friends(Name) VALUES ('Tom');"
"INSERT INTO Friends(Name) VALUES ('Rebecca');"
"INSERT INTO Friends(Name) VALUES ('Jim');"
"INSERT INTO Friend(Name) VALUES ('Robert');"
"COMMIT;";
```
第一個語句刪除`Friends`表(如果存在)。 其他語句放在事務內。 事務以全有或全無的方式工作。 要么什么都不做,要么什么都不做。
```c
sqlite> .tables
Cars Images
```
由于最后一條語句有錯誤,因此將回滾事務,并且不會創建`Friends`表。
## 數據來源
[SQLite 文檔](http://www.sqlite.org/docs.html)用于創建本教程。
[Tweet](https://twitter.com/share)
這是 SQLite C 教程。 ZetCode 擁有用于 SQLite Python 的完整電子書:
[SQLite Python 電子書](/ebooks/sqlitepython/)。
- ZetCode 數據庫教程
- MySQL 教程
- MySQL 簡介
- MySQL 安裝
- MySQL 的第一步
- MySQL 快速教程
- MySQL 存儲引擎
- MySQL 數據類型
- 在 MySQL 中創建,更改和刪除表
- MySQL 表達式
- 在 MySQL 中插入,更新和刪除數據
- MySQL 中的SELECT語句
- MySQL 子查詢
- MySQL 約束
- 在 MySQL 中導出和導入數據
- 在 MySQL 中連接表
- MySQL 函數
- MySQL 中的視圖
- MySQL 中的事務
- MySQL 存儲過程
- MySQL Python 教程
- MySQL Perl 教程
- MySQL & Perl DBI
- 使用 Perl 連接到 MySQL 數據庫
- MySQL 中的 Perl 錯誤處理
- 使用 Perl 進行 MySQL 查詢
- 在 MySQL 中使用 Perl 綁定參數&列
- 在 MySQL 中使用 Perl 處理圖像
- 使用 Perl 獲取 MySQL 元數據
- Perl 的 MySQL 事務
- MySQL C API 編程教程
- MySQL Visual Basic 教程
- MySQL PHP 教程
- MySQL Java 教程
- MySQL Ruby 教程
- MySQL C# 教程
- SQLite 教程
- SQLite 簡介
- sqlite3 命令行工具
- 在 SQLite 中創建,刪除和更改表
- SQLite 表達式
- SQLite 插入,更新,刪除數據
- SQLite SELECT語句
- SQLite 約束
- SQLite 連接表
- SQLite 函數
- SQLite 視圖,觸發器,事務
- SQLite C 教程
- SQLite Python 教程
- SQLite Perl 教程
- Perl DBI
- 使用 Perl 連接到 SQLite 數據庫
- SQLite Perl 錯誤處理
- 使用 Perl 的 SQLite 查詢
- 使用 Perl 綁定 SQLite 參數&列
- 使用 Perl 在 SQLite 中處理圖像
- 使用 Perl 獲取 SQLite 元數據
- 使用 Perl 進行 SQLite 事務
- SQLite Ruby 教程
- 連接到 SQLite 數據庫
- 在 SQLite 中使用 Ruby 進行 SQL 查詢
- 綁定參數
- 處理圖像
- 使用 Ruby 獲取 SQLite 元數據
- Ruby 的 SQLite 事務
- SQLite C# 教程
- SQLite C# 簡介
- 使用SqliteDataReader檢索數據
- ADO.NET 數據集
- 使用 C# 在 SQLite 中處理圖像
- 使用 C# 獲取 SQLite 元數據
- 使用 C# 的 SQLite 事務
- SQLite Visual Basic 教程
- SQLite Visual Basic 簡介
- 使用SqliteDataReader檢索數據
- ADO.NET 的數據集
- 使用 Visual Basic 在 SQLite 中處理圖像
- 使用 Visual Basic 獲取 SQLite 元數據
- 使用 Visual Basic 的 SQLite 事務
- PostgreSQL C 教程
- PostgreSQL Ruby 教程
- PostgreSQL PHP 教程
- PostgreSQL PHP 編程簡介
- 在 PostgreSQL 中使用 PHP 檢索數據
- 在 PostgreSQL 中使用 PHP 處理圖像
- 用 PHP 獲取 PostgreSQL 元數據
- 在 PostgreSQL 中使用 PHP 進行事務
- PostgreSQL Java 教程
- Apache Derby 教程
- Derby 簡介
- Derby 的安裝&配置
- Derby 工具
- ij 工具
- Derby 中的 SQL 查詢
- 在 Derby 中使用 JDBC 進行編程
- Derby 安全
- 使用 Derby & Apache Tomcat
- NetBeans 和 Derby
- SQLAlchemy 教程
- SQLAlchemy 簡介
- 原始 SQL
- 模式定義語言
- SQL 表達式語言
- SQLAlchemy 中的對象關系映射器
- MongoDB PHP 教程
- MongoDB JavaScript 教程
- MongoDB Ruby 教程
- Spring JdbcTemplate 教程
- JDBI 教程
- MyBatis 教程
- Hibernate Derby 教程
- ZetCode .NET 教程
- Visual Basic 教程
- Visual Basic
- Visual Basic 語法結構
- 基本概念
- Visual Basic 數據類型
- Visual Basic 中的字符串
- 運算符
- 控制流
- Visual Basic 數組
- Visual Basic 中的過程&函數
- 在 Visual Basic 中組織代碼
- 面向對象編程
- Visual Basic 中的面向對象編程 II
- Visual Basic 中的集合
- 輸入和輸出
- C# 教程
- C# 語言
- C# 語法結構
- C# 基礎
- C# 數據類型
- C# 中的字符串
- C# 運算符
- C# 中的流控制
- C# 數組
- C# 面向對象編程
- C# 中的方法
- C# 面向對象編程 II
- C# 屬性
- C# 結構
- C# 委托
- 命名空間
- C# 集合
- C# 輸入和輸出
- C# 目錄教程
- C# 字典教程
- 在 C# 中讀取文本文件
- C# 中的日期和時間
- 在 C# 中讀取網頁
- C# HttpClient教程
- ASP.NET Core 教程
- ZetCode 圖形教程
- Java 2D 游戲教程
- Java 游戲基礎
- 動畫
- 移動精靈
- 碰撞檢測
- Java 益智游戲
- Java Snake
- Breakout 游戲
- Java 俄羅斯方塊
- Java 吃豆人
- Java 太空侵略者
- Java 掃雷
- Java 推箱子
- Java 2D 教程
- 介紹
- 基本繪圖
- 形狀和填充
- 透明度
- 合成
- 剪裁
- 變換
- 特效
- 圖像
- 文字和字體
- 命中測試,移動物體
- 俄羅斯方塊
- Cario 圖形教程
- Cario 圖形庫
- Cario 定義
- Cairo 后端
- Cairo 基本圖形
- 形狀和填充
- 漸變
- 透明度
- 合成
- 剪裁和遮罩
- 變換
- Cairo 文字
- Cairo 中的圖像
- 根窗口
- PyCairo 教程
- PyCairo 簡介
- PyCairo 后端
- PyCairo 中的基本繪圖
- PyCairo 形狀和填充
- PyCairo 漸變
- PyCairo 剪裁&遮罩
- PyCairo 的透明度
- PyCairo 中的變換
- PyCairo 中的文字
- PyCairo 中的圖像
- 根窗口
- HTML5 畫布教程
- 介紹
- HTML5 畫布中的直線
- HTML5 畫布形狀
- HTML5 畫布填充
- HTML5 畫布中的透明度
- HTML5 畫布合成
- HTML5 canvas 中的變換
- HTML5 畫布中的文字
- HTML5 畫布中的動畫
- HTML5 畫布中的 Snake
- ZetCode GUI 教程
- Windows API 教程
- Windows API 簡介
- Windows API main函數
- Windows API 中的系統函數
- Windows API 中的字符串
- Windows API 中的日期和時間
- Windows API 中的一個窗口
- UI 的第一步
- Windows API 菜單
- Windows API 對話框
- Windows API 控件 I
- Windows API 控件 II
- Windows API 控件 III
- Windows API 中的高級控件
- Windows API 中的自定義控件
- Windows API 中的 GDI
- PyQt4 教程
- PyQt4 簡介
- PyQt4 中的第一個程序
- PyQt4 中的菜單和工具欄
- PyQt4 中的布局管理
- PyQt4 中的事件和信號
- PyQt4 中的對話框
- PyQt4 小部件
- PyQt4 小部件 II
- PyQt4 中的拖放
- PyQt4 中的繪圖
- PyQt4 中的自定義小部件
- PyQt4 中的俄羅斯方塊游戲
- PyQt5 教程
- PyQt5 簡介
- PyQt5 日期和時間
- PyQt5 中的第一個程序
- PyQt5 中的菜單和工具欄
- PyQt5 中的布局管理
- PyQt5 中的事件和信號
- PyQt5 中的對話框
- PyQt5 小部件
- PyQt5 小部件 II
- PyQt5 拖放
- PyQt5 中的繪圖
- PyQt5 中的自定義小部件
- PyQt5 中的俄羅斯方塊
- Qt4 教程
- Qt4 工具包簡介
- Qt4 工具類
- Qt4 中的字符串
- Qt4 中的日期和時間
- 在 Qt4 中使用文件和目錄
- Qt4 中的第一個程序
- Qt4 中的菜單和工具欄
- Qt4 中的布局管理
- Qt4 中的事件和信號
- Qt4 小部件
- Qt4 小部件 II
- Qt4 中的繪圖
- Qt4 中的自定義小部件
- Qt4 中的打磚塊游戲
- Qt5 教程
- Qt5 工具包簡介
- Qt5 中的字符串
- Qt5 中的日期和時間
- Qt5 中的容器
- 在 Qt5 中處理文件和目錄
- Qt5 中的第一個程序
- Qt5 中的菜單和工具欄
- Qt5 中的布局管理
- Qt5 中的事件和信號
- Qt5 小部件
- Qt5 小部件 II
- Qt5 中的繪圖
- Qt5 中的自定義小部件
- Qt5 中的貪食蛇
- Qt5 中的打磚塊游戲
- PySide 教程
- PySide 工具包簡介
- PySide 中的第一個程序
- PySide 中的菜單和工具欄
- PySide 中的布局管理
- PySide 中的事件和信號
- PySide 中的對話框
- PySide 小部件
- PySide 小部件 II
- 在 PySide 中拖放
- 在 PySide 中繪圖
- PySide 中的自定義小部件
- PySide 中的俄羅斯方塊游戲
- Tkinter 教程
- Tkinter 簡介
- Tkinter 中的布局管理
- Tkinter 標準小部件屬性
- Tkinter 小部件
- Tkinter 中的菜單和工具欄
- Tkinter 中的對話框
- Tkinter 中的繪圖
- Tkinter 中的貪食蛇
- Tcl/Tk 教程
- Tcl/Tk 簡介
- Tcl/Tk 中的布局管理
- Tcl/Tk 小部件
- Tcl/Tk 中的菜單和工具欄
- Tcl/Tk 中的對話框
- Tcl/Tk 繪圖
- 貪食蛇
- Qt 快速教程
- Java Swing 教程
- Java Swing 簡介
- Java Swing 首個程序
- Java Swing 中的菜單和工具欄
- Swing 布局管理
- GroupLayout管理器
- Java Swing 事件
- 基本的 Swing 組件
- 基本的 Swing 組件 II
- Java Swing 對話框
- Java Swing 模型架構
- Swing 中的拖放
- Swing 中的繪圖
- Java Swing 中的可調整大小的組件
- Java Swing 中的益智游戲
- 俄羅斯方塊
- JavaFX 教程
- JavaFX 簡介
- JavaFX 首個程序
- JavaFX 布局窗格
- 基本的 JavaFX 控件
- 基本 JavaFX 控件 II
- JavaFX 事件
- JavaFX 效果
- JavaFX 動畫
- JavaFX 畫布
- JavaFX 圖表
- Java SWT 教程
- Java SWT 簡介
- Java SWT 中的布局管理
- Java SWT 中的菜單和工具欄
- Java SWT 中的小部件
- Table小部件
- Java SWT 中的對話框
- Java SWT 繪圖
- Java SWT 中的貪食蛇
- wxWidgets 教程
- wxWidgets 簡介
- wxWidgets 助手類
- wxWidgets 中的第一個程序
- wxWidgets 中的菜單和工具欄
- wxWidgets 中的布局管理
- wxWidgets 中的事件
- wxWidgets 中的對話框
- wxWidgets 小部件
- wxWidgets 小部件 II
- wxWidgets 中的拖放
- wxWidgets 中的設備上下文
- wxWidgets 中的自定義小部件
- wxWidgets 中的俄羅斯方塊游戲
- wxPython 教程
- wxPython 簡介
- 第一步
- 菜單和工具欄
- wxPython 中的布局管理
- wxPython 中的事件
- wxPython 對話框
- 小部件
- wxPython 中的高級小部件
- wxPython 中的拖放
- wxPython 圖形
- 創建自定義小部件
- wxPython 中的應用框架
- wxPython 中的俄羅斯方塊游戲
- C# Winforms Mono 教程
- Mono Winforms 簡介
- Mono Winforms 中的第一步
- Mono Winforms 中的布局管理
- Mono Winforms 中的菜單和工具欄
- Mono Winforms 中的基本控件
- Mono Winforms 中的高級控件
- 對話框
- Mono Winforms 中的拖放
- Mono Winforms 中的繪圖
- Mono Winforms 中的貪食蛇
- Java Gnome 教程
- Java Gnome 簡介
- Java Gnome 的第一步
- Java Gnome 中的布局管理
- Java Gnome 中的布局管理 II
- Java Gnome 中的菜單
- Java Gnome 中的工具欄
- Java Gnome 中的事件
- Java Gnome 中的小部件
- Java Gnome 中的小部件 II
- Java Gnome 中的高級小部件
- Java Gnome 中的對話框
- Java Gnome 中的 Pango
- 在 Java Gnome 中用 Cairo 繪圖
- Cario 繪圖 II
- Java Gnome 中的貪食蛇
- QtJambi 教程
- QtJambi 簡介
- QtJambi 中的布局管理
- QtJambi 中的小部件
- QtJambi 中的菜單和工具欄
- QtJambi 對話框
- QtJambi 中的繪圖
- QtJambi 中的自定義小部件
- 貪食蛇
- GTK+ 教程
- GTK+ 簡介
- GTK+ 中的第一個程序
- GTK+ 中的菜單和工具欄
- GTK+ 布局管理
- GTK+ 事件和信號
- GTK+ 對話框
- GTK+ 小部件
- GTK+ 小部件 II
- GtkTreeView小部件
- GtkTextView小部件
- 自定義 GTK+ 小部件
- Ruby GTK 教程
- Ruby GTK 簡介
- Ruby GTK 中的布局管理
- Ruby GTK 中的小部件
- Ruby GTK 中的菜單和工具欄
- Ruby GTK 中的對話框
- Ruby GTK Cario 繪圖
- Ruby GTK 中的自定義小部件
- Ruby GTK 中的貪食蛇
- GTK# 教程
- GTK# 簡介
- GTK 的第一步
- GTK# 中的布局管理
- GTK 中的菜單
- GTK# 中的工具欄
- GTK# 中的事件
- GTK# 中的小部件
- GTK 中的小部件 II
- GTK# 中的高級小部件
- GTK# 中的對話框
- Pango
- GTK# 中的 Cario 繪圖
- GTK# 中的 Cario 繪圖 II
- GTK# 中的自定義小部件
- Visual Basic GTK# 教程
- Visual Basic GTK# 簡介
- 布局管理
- 小部件
- 菜單和工具欄
- 對話框
- Cario 繪圖
- 自定義小部件
- 貪食蛇
- PyGTK 教程
- PyGTK 簡介
- PyGTK 的第一步
- PyGTK 中的布局管理
- PyGTK 中的菜單
- PyGTK 中的工具欄
- PyGTK 中的事件和信號
- PyGTK 中的小部件
- PyGTK 中的小部件 II
- PyGTK 中的高級小部件
- PyGTK 中的對話框
- Pango
- Pango II
- PyGTK 中的 Cario 繪圖
- Cario 繪圖 II
- PyGTK 中的貪食蛇游戲
- PyGTK 中的自定義小部件
- PHP GTK 教程
- PHP GTK 簡介
- PHP GTK 中的布局管理
- PHP GTK 中的小部件
- PHP GTK 中的菜單和工具欄
- 對話框
- Cario 繪圖
- 自定義小部件
- 貪食蛇
- C# Qyoto 教程
- Qyoto 介紹
- 布局管理
- Qyoto 中的小部件
- Qyoto 中的菜單和工具欄
- Qyoto 對話框
- Qyoto 中的繪圖
- Qyoto 中的繪圖 II
- Qyoto 中的自定義小部件
- 貪食蛇
- Ruby Qt 教程
- Ruby Qt 簡介
- Ruby Qt 中的布局管理
- Ruby Qt 中的小部件
- 菜單和工具欄
- Ruby Qt 中的對話框
- 用 Ruby Qt 繪圖
- Ruby Qt 中的自定義小部件
- Ruby Qt 中的貪食蛇
- Visual Basic Qyoto 教程
- Qyoto 介紹
- 布局管理
- Qyoto 中的小部件
- Qyoto 中的菜單和工具欄
- Qyoto 對話框
- Qyoto 中的繪圖
- Qyoto 中的自定義小部件
- 貪食蛇
- Mono IronPython Winforms 教程
- 介紹
- IronPython Mono Winforms 中的第一步
- 布局管理
- 菜單和工具欄
- Mono Winforms 中的基本控件
- Mono Winforms 中的基本控件 II
- Mono Winforms 中的高級控件
- 對話框
- Mono Winforms 中的拖放
- 繪圖
- IronPython Mono Winforms 中的繪圖 II
- IronPython Mono Winforms 中的貪食蛇
- IronPython Mono Winforms 中的俄羅斯方塊游戲
- FreeBASIC GTK 教程
- Jython Swing 教程
- Jython Swing 簡介
- Jython Swing 中的布局管理
- Jython Swing 中的組件
- Jython Swing 中的菜單和工具欄
- Jython Swing 中的對話框
- Jython Swing 中的繪圖
- Jython Swing 中的半字節
- JRuby Swing 教程
- JRuby Swing 簡介
- JRuby Swing 中的布局管理
- JRuby Swing 中的組件
- 菜單和工具欄
- JRuby Swing 中的對話框
- 在 JRuby Swing 中繪圖
- JRuby Swing 中的貪食蛇
- Visual Basic Winforms 教程
- Visual Basic Winforms 簡介
- 布局管理
- 基本控制
- 進階控件
- 菜單和工具欄
- 對話框
- 繪圖
- 拖放
- 貪食蛇
- JavaScript GTK 教程
- JavaScript GTK 簡介
- 布局管理
- JavaScript GTK 中的小部件
- JavaScript GTK 中的菜單和工具欄
- JavaScript GTK 中的對話框
- JavaScript GTK 中的 Cario 繪圖
- ZetCode Java 教程
- Java 教程
- Java 語言
- Java 語法結構
- Java 基礎
- Java 數據類型
- Java 數據類型 II
- Java 字符串
- Java 數組
- Java 表達式
- Java 控制流程
- Java 面向對象的編程
- Java 方法
- Java 面向對象編程 II
- Java 包
- Java 中的異常
- Java 集合
- Java 流
- Java Future 教程
- Java Comparable和Comparator
- Java DOM 教程
- Java MVC 教程
- Java SAX 教程
- Java JAXB 教程
- Java JSON 處理教程
- Java H2 教程
- MongoDB Java 教程
- Java 正則表達式教程
- Java PDFBox 教程
- Java 文件教程
- Java Files.list教程
- Java Files.walk教程
- Java DirectoryStream教程
- Java 外部與內部迭代器
- Java 文件大小
- 用 Java 創建目錄
- 用 Java 創建文件
- Java Log4j 教程
- Gson 教程
- Java RequestDispatcher
- Java HTTP GET/POST 請求
- Java InputStream教程
- Java FileOutputStream教程
- Java FileInputStream教程
- Java ZipInputStream教程
- Java FileWriter教程
- EJB 簡介
- Java forEach教程
- Jetty 教程
- Tomcat Derby 教程
- Stripes 介紹
- 使用 Stripes 的 Java webapp,MyBatis,& Derby
- EclipseLink 簡介
- Java 中的數據源
- JSTL 中的 SQL 查詢標記
- Java 驗證過濾器
- Hibernate 驗證器
- 用 Java 顯示圖像
- Play 框架簡介
- Spark Java 簡介
- Java ResourceBundle教程
- Jtwig 教程
- Java Servlet 教程
- Java 套接字教程
- FreeMarker 教程
- Android 教程
- Java EE 5 教程
- JSoup 教程
- JFreeChart 教程
- ImageIcon教程
- 用 Java 復制文件
- Java 文件時間教程
- 如何使用 Java 獲取當前日期時間
- Java 列出目錄內容
- Java 附加到文件
- Java ArrayList教程
- 用 Java 讀寫 ICO 圖像
- Java int到String的轉換
- Java HashSet教程
- Java HashMap教程
- Java static關鍵字
- Java 中的HashMap迭代
- 用 Java 過濾列表
- 在 Java 中讀取網頁
- Java 控制臺應用
- Java 集合的便利工廠方法
- Google Guava 簡介
- OpenCSV 教程
- 用 Java8 的StringJoiner連接字符串
- Java 中元素迭代的歷史
- Java 謂詞
- Java StringBuilder
- Java 分割字串教學
- Java NumberFormat
- Java TemporalAdjusters教程
- Apache FileUtils教程
- Java Stream 過濾器
- Java 流歸約
- Java 流映射
- Java InputStreamReader教程
- 在 Java 中讀取文本文件
- Java Unix 時間
- Java LocalTime
- Java 斐波那契
- Java ProcessBuilder教程
- Java 11 的新功能
- ZetCode JavaScript 教程
- Ramda 教程
- Lodash 教程
- Collect.js 教程
- Node.js 簡介
- Node HTTP 教程
- Node-config 教程
- Dotenv 教程
- Joi 教程
- Liquid.js 教程
- faker.js 教程
- Handsontable 教程
- PouchDB 教程
- Cheerio 教程
- Axios 教程
- Jest 教程
- JavaScript 正則表達式
- 用 JavaScript 創建對象
- Big.js 教程
- Moment.js 教程
- Day.js 教程
- JavaScript Mustache 教程
- Knex.js 教程
- MongoDB JavaScript 教程
- Sequelize 教程
- Bookshelf.js 教程
- Node Postgres 教程
- Node Sass 教程
- Document.querySelector教程
- Document.all教程
- JSON 服務器教程
- JavaScript 貪食蛇教程
- JavaScript 構建器模式教程
- JavaScript 數組
- XMLHttpRequest教程
- 從 JavaScript 中的 URL 讀取 JSON
- 在 JavaScript 中循環遍歷 JSON 數組
- jQuery 教程
- Google 圖表教程
- ZetCode Kotlin 教程
- Kotlin Hello World 教程
- Kotlin 變量
- Kotlin 的運算符
- Kotlin when表達式
- Kotlin 數組
- Kotlin 范圍
- Kotlin Snake
- Kotlin Swing 教程
- Kotlin 字符串
- Kotlin 列表
- Kotlin 映射
- Kotlin 集合
- Kotlin 控制流程
- Kotlin 寫入文件
- Kotlin 讀取文件教程
- Kotlin 正則表達式
- ZetCode 其它教程
- TCL 教程
- Tcl
- Tcl 語法結構
- Tcl 中的基本命令
- Tcl 中的表達式
- Tcl 中的控制流
- Tcl 中的字符串
- Tcl 列表
- Tcl 中的數組
- Tcl 中的過程
- 輸入&輸出
- AWK 教程
- Vaadin 教程
- Vaadin 框架介紹
- Vaadin Grid教程
- Vaadin TextArea教程
- Vaadin ComboBox教程
- Vaadin Slider教程
- Vaadin CheckBox教程
- Vaadin Button教程
- Vaadin DateField教程
- Vaadin Link教程
- ZetCode PHP 教程
- PHP 教程
- PHP
- PHP 語法結構
- PHP 基礎
- PHP 數據類型
- PHP 字符串
- PHP 運算符
- PHP 中的控制流
- PHP 數組
- PHP 數組函數
- PHP 中的函數
- PHP 正則表達式
- PHP 中的面向對象編程
- PHP 中的面向對象編程 II
- PHP Carbon 教程
- PHP Monolog 教程
- PHP 配置教程
- PHP Faker 教程
- Twig 教程
- Valitron 教程
- Doctrine DBAL QueryBuilder 教程
- PHP Respect 驗證教程
- PHP Rakit 驗證教程
- PHP PDO 教程
- CakePHP 數據庫教程
- PHP SQLite3 教程
- PHP 文件系統函數
- ZetCode Python 教程
- Python 教程
- Python 語言
- 交互式 Python
- Python 語法結構
- Python 數據類型
- Python 字符串
- Python 列表
- Python 字典
- Python 運算符
- Python 關鍵字
- Python 函數
- Python 中的文件
- Python 中的面向對象編程
- Python 模塊
- Python 中的包
- Python 異常
- Python 迭代器和生成器
- Python 內省
- Python Faker 教程
- Python f 字符串教程
- Python bcrypt 教程
- Python 套接字教程
- Python smtplib教程
- OpenPyXL 教程
- Python pathlib教程
- Python YAML 教程
- Python 哈希教程
- Python ConfigParser教程
- Python 日志教程
- Python argparse 教程
- Python SQLite 教程
- Python Cerberus 教程
- Python PostgreSQL 教程
- PyMongo 教程
- PyMySQL 教程
- Peewee 教程
- pyDAL 教程
- pytest 教程
- Bottle 教程
- Python Jinja 教程
- PrettyTable 教程
- BeautifulSoup 教程
- pyquery 教程
- Python for循環
- Python 反轉
- Python Lambda 函數
- Python 集合
- Python 映射
- Python CSV 教程-讀寫 CSV
- Python 正則表達式
- Python SimpleJson 教程
- SymPy 教程
- Pandas 教程
- Matplotlib 教程
- Pillow 教程
- Python FTP 教程
- Python Requests 教程
- Python Arrow 教程
- Python 列表推導式
- Python 魔術方法
- PyQt 中的QPropertyAnimation
- PyQt 中的QNetworkAccessManager
- ZetCode Ruby 教程
- Ruby 教程
- Ruby
- Ruby 語法結構
- Ruby 基礎
- Ruby 變量
- Ruby 中的對象
- Ruby 數據類型
- Ruby 字符串
- Ruby 表達式
- Ruby 控制流
- Ruby 數組
- Ruby 哈希
- Ruby 中的面向對象編程
- Ruby 中的面向對象編程 II
- Ruby 正則表達式
- Ruby 輸入&輸出
- Ruby HTTPClient教程
- Ruby Faraday 教程
- Ruby Net::HTTP教程
- ZetCode Servlet 教程
- 從 Java Servlet 提供純文本
- Java Servlet JSON 教程
- Java Servlet HTTP 標頭
- Java Servlet 復選框教程
- Java servlet 發送圖像教程
- Java Servlet JQuery 列表教程
- Servlet FreeMarker JdbcTemplate 教程-CRUD 操作
- jQuery 自動補全教程
- Java servlet PDF 教程
- servlet 從 WAR 內讀取 CSV 文件
- Java HttpServletMapping
- EasyUI datagrid
- Java Servlet RESTFul 客戶端
- Java Servlet Log4j 教程
- Java Servlet 圖表教程
- Java ServletConfig教程
- Java Servlet 讀取網頁
- 嵌入式 Tomcat
- Java Servlet 分頁
- Java Servlet Weld 教程
- Java Servlet 上傳文件
- Java Servlet 提供 XML
- Java Servlet 教程
- JSTL forEach標簽
- 使用 jsGrid 組件
- ZetCode Spring 教程
- Spring @Bean注解教程
- Spring @Autowired教程
- Spring @GetMapping教程
- Spring @PostMapping教程
- Spring @DeleteMapping教程
- Spring @RequestMapping教程
- Spring @PathVariable教程
- Spring @RequestBody教程
- Spring @RequestHeader教程
- Spring Cookies 教程
- Spring 資源教程
- Spring 重定向教程
- Spring 轉發教程
- Spring ModelAndView教程
- Spring MessageSource教程
- Spring AnnotationConfigApplicationContext
- Spring BeanFactoryPostProcessor教程
- Spring BeanFactory教程
- Spring context:property-placeholder教程
- Spring @PropertySource注解教程
- Spring @ComponentScan教程
- Spring @Configuration教程
- Spring C 命名空間教程
- Spring P 命名空間教程
- Spring bean 引用教程
- Spring @Qualifier注解教程
- Spring ClassPathResource教程
- Spring 原型作用域 bean
- Spring Inject List XML 教程
- Spring 概要文件 XML 教程
- Spring BeanDefinitionBuilder教程
- Spring 單例作用域 bean
- 獨立的 Spring 應用
- 經典 Spring 應用中的JdbcTemplate
- Spring EmbeddedDatabaseBuilder教程
- Spring HikariCP 教程
- Spring Web 應用簡介
- Spring BeanPropertyRowMapper教程
- Spring DefaultServlet教程
- Spring WebSocket 教程
- Spring WebJars 教程
- Spring @MatrixVariable教程
- Spring Jetty 教程
- Spring 自定義 404 錯誤頁面教程
- Spring WebApplicationInitializer教程
- Spring BindingResult教程
- Spring FreeMarker 教程
- Spring Thymeleaf 教程
- Spring ResourceHandlerRegistry教程
- SpringRunner 教程
- Spring MockMvc 教程
- ZetCode Spring Boot 教程
- Spring Boot 發送電子郵件教程
- Spring Boot WebFlux 教程
- Spring Boot ViewControllerRegistry教程
- Spring Boot CommandLineRunner教程
- Spring Boot ApplicationReadyEvent 教程
- Spring Boot CORS 教程
- Spring Boot @Order教程
- Spring Boot @Lazy教程
- Spring Boot Flash 屬性
- Spring Boot CrudRepository 教程
- Spring Boot JpaRepository 教程
- Spring Boot findById 教程
- Spring Boot Data JPA @NamedQuery教程
- Spring Boot Data JPA @Query教程
- Spring Boot Querydsl 教程
- Spring Boot Data JPA 排序教程
- Spring Boot @DataJpaTest教程
- Spring Boot TestEntityManager 教程
- Spring Boot Data JPA 派生的查詢
- Spring Boot Data JPA 查詢示例
- Spring Boot Jersey 教程
- Spring Boot CSV 教程
- SpringBootServletInitializer教程
- 在 Spring Boot 中加載資源
- Spring Boot H2 REST 教程
- Spring Boot RestTemplate
- Spring Boot REST XML 教程
- Spring Boot Moustache 教程
- Spring Boot Thymeleaf 配置
- Spring Boot 自動控制器
- Spring Boot FreeMarker 教程
- Spring Boot Environment
- Spring Boot Swing 集成教程
- 在 Spring Boot 中提供圖像文件
- 在 Spring Boot 中創建 PDF 報告
- Spring Boot 基本注解
- Spring Boot @ResponseBody教程
- Spring Boot @PathVariable教程
- Spring Boot REST Data JPA 教程
- Spring Boot @RequestParam教程
- Spring Boot 列出 bean
- Spring Boot @Bean
- Spring Boot @Qualifier教程
- 在 Spring Boot 中提供靜態內容
- Spring Boot Whitelabel 錯誤
- Spring Boot DataSourceBuilder 教程
- Spring Boot H2 教程
- Spring Boot Web JasperReports 集成
- Spring Boot iText 教程
- Spring Boot cmd JasperReports 集成
- Spring Boot RESTFul 應用
- Spring Boot 第一個 Web 應用
- Spring Boot Groovy CLI
- Spring Boot 上傳文件
- Spring Boot @ExceptionHandler
- Spring Boot @ResponseStatus
- Spring Boot ResponseEntity
- Spring Boot @Controller
- Spring Boot @RestController
- Spring Boot @PostConstruct
- Spring Boot @Component
- Spring Boot @ConfigurationProperties教程
- Spring Boot @Repository
- Spring Boot MongoDB 教程
- Spring Boot MongoDB Reactor 教程
- Spring Boot PostgreSQL 教程
- Spring Boot @ModelAttribute
- Spring Boot 提交表單教程
- Spring Boot Model
- Spring Boot MySQL 教程
- Spring Boot GenericApplicationContext
- SpringApplicationBuilder教程
- Spring Boot Undertow 教程
- Spring Boot 登錄頁面教程
- Spring Boot RouterFunction 教程
- ZetCode Symfony 教程
- Symfony DBAL 教程
- Symfony 表單教程
- Symfony CSRF 教程
- Symfony Vue 教程
- Symfony 簡介
- Symfony 請求教程
- Symfony HttpClient教程
- Symfony Flash 消息
- 在 Symfony 中發送郵件
- Symfony 保留表單值
- Symfony @Route注解教程
- Symfony 創建路由
- Symfony 控制臺命令教程
- Symfony 上傳文件
- Symfony 服務教程
- Symfony 驗證教程
- Symfony 翻譯教程