CGI

1
2
3
char * qs = getenv("QUERY_STRING");//获取环境变量
char username[256];
sscanf(qs, "username=%s", username);//获取地址栏后面的东西

断点调试:include <windows.h>,然后 MessageBoxA(0,"ok","ok",0);
让程序启动后暂停,等待着按按钮;
浏览器访问CGI,窗口弹出后,在VS中“调试”→“附加到进程”,设置断点,然后在关闭消息对话框。

1
2
3
printf("Location:http://www.baidu.com\r\n");//重定向,302 Found。注意\r\n
printf("Location:1.html\r\n");//重定向至cgi程序同目录的1.html文件
printf("Content-Type:text/html;charset=gbk\r\n\r\n");//改成 text/plain,为普通文本,显示HTML标签。

注意\r\n\r\n(两个),表示报文头结束了(各个报文头顺序可调换)
源代码中,\n能换行,但是浏览器上要用<br>才换行,所以用: \n<br>

1
2
itoa(i, str, x); //int i 转换成 char str[]、x进制的文本
int i = atoi(str); //字符串转换成int
1
fprintf(cgiOut, "<html><head></head><body>大家好,Querystring=%s,你的浏览器UserAgent是:%s,你的IP地址是:%s,当前cgi地址:%s</body></html>", cgiQueryString, cgiUserAgent, cgiRemoteAddr, cgiScriptName);//可以用printf
1
2
cgiFormString("name", name, sizeof name); // 获取用户名:name=xxx
if (cgiFormString("password", pwd, sizeof pwd) != cgiFormSuccess);//返回值表示有没有这个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  <!--点击登陆转到网址:../11.cgi?name=admin&pwd=123,
或者绝对网址:"http://www.baidu.com"。
method="get"(默认)或者"post",post时地址栏看不到表单 -->
<form action="11.cgi">
用户名:<input type="text" name="name" value="admin" />
密 码:<input type="password" name="pwd" value="123" />
<input type="submit" value="登陆" />
</form>
<!--form中的:input、textarea、select 中的名字和值都会提交给服务器
没有name值的表单将不会提交,包括submit。-->

<select name="se"><option value="nan"></option><option... /option></select>
<!--提交的值为value属性,如:se="nan"-->
<input type="checkbox" name="ch" id="chk" / ><lable for="chk">我已阅读XXX</lable>
<!--如果勾上了,会有 "ch=on" 提交给服务器。
radio 的 name 属性相同的为一组,提交选中的 value-->
<input type="hidden" ... />
<!--隐藏的字段,不会显示,但是也会提交-->

1、select、input、textarea必须要在form中。
2、当点击input type="submit"的按钮的时候,会把它所在的form中的所有“有name属性的input/select/textarea的值(value)提交给服务器。
3、表单提交的值

  • input type="text"的值就是用户输入的值;
  • 对于input type="checkbox"的值就是“选中是on,没选中则没有”;
  • 对于inpu type="radio“的则是被选中的name、value提交给服务器;
  • 对于input type="submit"的则是被点击的name、value被提交给服务器;
  • 对textarea就是输入的文本;
  • 对于select就是选择项对应的option的value。
1
2
3
4
5
6
TMPL_varlist *varlist1 = 0;//空的参数列表 C99
cgiHeaderContentType("text/html;charset=gbk");
varlist1 = TMPL_add_var(varlist1, "test1", "特斯特1", "pwd", "小密码", "name", "小鹏", 0); // 3对,最后一个必须为0表示可变
char *age = "38";
varlist1 = TMPL_add_var(varlist1, "age", age, 0);//执行完了varlist1四对参数
TMPL_write("P1.htm", 0, 0, varlist1, cgiOut, cgiOut);

变量使用:<TMPL_VAR name="test1" /><br />

1
2
3
4
5
6
7
8
9
TMPL_varlist * varlist1 = 0;
TMPL_loop * loopUsers = 0;
varlist1 = TMPL_add_var(varlist1, "Title", "测试Loop", 0);
loopUsers = TMPL_add_varlist(loopUsers, TMPL_add_var(0, "Name", "如鹏网", "Age", "8", "Title", "t1", 0));//t1,同一行有的用同一行
loopUsers = TMPL_add_varlist(loopUsers, TMPL_add_var(0, "Name", "淘宝", "Age", "15", "Title", "t2", 0));//t2
loopUsers = TMPL_add_varlist(loopUsers, TMPL_add_var(0, "Name", "京东", "Age", "10", 0));//测试Loop,同一行没有的到上层去找
varlist1 = TMPL_add_loop(varlist1, "users", loopUsers);//大List里面的小List(name=“users”的Loop)
cgiHeaderContentType("text/html;charset=gbk");
TMPL_write("Users.htm", 0, 0, varlist1, cgiOut, cgiOut);
1
2
3
4
5
<table><tbody><TMPL_LOOP name="users">
<tr><td><TMPL_VAR name="Name" /></td>
<td><TMPL_VAR name="Age" /></td>
<td><TMPL_VAR name="Title" /></td>
</tr> </TMPL_LOOP></tbody> </table>

MySQL 数据库

1
select * from Students // 表里面的数据
1
insert into Students(ID, Name, Gender) value(3, 'asd', 1) // 插入数据,属性值对应(允许为空或者自动递增可不用写)
1
2
3
update Persons set Name='hehehe',Age=Age+1 // 全部更新表中某几列的数据
update Persons set Age=Age+1 where Name='Tom' // 名字为 Tom 的人年龄+1
条件用法:where (Age>20 and Age<30) or(Age=80) // or、and、not、<、>、>=、<=、!=(或<>)
1
2
delete from Person where ... // 删除表中的数据,没有where为删除所有
drop table Persons // 删除表
1
2
3
4
select Age+1, Name from Persons where...// 显示表中的某几列(Age的列结果为Age+1)
select Name 姓名, Age as 年龄 from Persons; // 为列取别名,可以不写as
select 1+1 // 检索不与表关联的数据,结果为2
select now() // 返回时间
1
2
3
4
5
SQL聚合函数:MAX(最大值)、MIN(最小值)、AVG (平均值)、SUM (和)、COUNT(数量)
select min(Age), max(Age) from Persons // 最低、最高年龄
select count(*) from Persons where Age>25 // 年龄大于25的人数
select sum(Age), avg(Age) from Persons // 总年龄和平均年龄
select count(Hobbies) from Persons // Hobbies 不为空的数量
1
2
select * from Persons (where……) order by Age ASC, Salary DESC // 按照年龄升序(ASC,不写时默认)、薪水降序(DESC) 排列
order 必须放在 where 后面
1
2
3
单字符通配符:_
多字符通配符:% (可以为0个)
select * from Persons where Name like 'T%' // 名字为 T 开头的所有
1
2
3
4
null 代表“不知道”,而不是“没有”。null+1 结果是 null
select ...... name=null 或者 name != null 或者 nul=null 都没有任何返回结果
select name+"a" from Persons
select * from Persons where name is (not) null // 判断是不是null
1
limit 首行行号(行号从 0 开始), 最大数目 // limit 必须放在所有语句的最后
1
2
group by Age   // 按照年龄分组,相同的为一组(重复的堆到一起)
select age, count(*) from Persons group by age // 显示所有年龄段,并且显示数量
1
2
3
4
5
查询每张订单的订单号、价格、对应的客户姓名以及客户年龄
SELECT o.Number 订单编号,o.Price 价格,c.Name 客户姓名,c.Age FROM T_Orders o // 表别名为 o
LEFT JOIN T_Customers c ON o.CustomerId=c.Id // 别名为 c,当两个相等的时候,进行连接
LEFT JOIN T_OrderTypes t ON o.TypeId=t.Id // 可以 join 多张表
WHERE o.Price>=150 // 可添加 where 条件
1
2
设计表——外键——添加,然后删除时会提示无法删除(可设置)
删除时、更新时:一般默认RESTRICT(一起删除);SET NULL(删除后连接的项目设置为NULL);NO ACTION/RESTRICT(拒绝删除)

C + MySQL

使用方法:需要修改三个目录,以及复制dll

1、在项目属性中【VC++目录】→“包含目录”,选择mysql的include文件夹;“库目录”选择mysql的lib文件夹;【链接器】→【输入】的“附加依赖项”增加“libmysql.lib”。

2、C代码里include头文件mysql.h,并且在之前要include头文件winsock.h

3、MYSQL *pConn = mysql_init(0);

4、编译运行,会报错找不到“libmysql.dll”,把mysql的libmysql.dll复制到exe的目录下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdlib.h>
#include <stdio.h>
#include <winsock.h>
#include <mysql.h>

int main()
{
MYSQL *mysql = mysql_init(0);
if(!mysql_real_connect(mysql,"localhost","root","root","study3",0,0,0)){
printf("连接数据库出错:%s",mysql_error(mysql)); // mysql的上一次错误信息
goto exit;//goto一般不推荐使用,但是在错误处理的时候,很好用
}
printf("连接数据库成功!\n");
if(mysql_query(mysql,"set names gbk")){ // 这样才能用中文。失败时 为 真
printf("设定连接编码失败%s",mysql_error(mysql));
goto exit;
}

exit:
mysql_close(mysql);//程序最后必须关闭连接,否则会有mysql服务器连接过多卡死的可能性
printf("exit");
getchar();
return 0;
}

execute封装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <stdio.h>
#include <stdlib.h>
#include <winsock.h>
#include <mysql.h>

void executeNonQuery(char * sql); // 没有返回值的
MYSQL_RES * executeQuery(char * sql); // 有返回值的

int main()
{
executeNonQuery("insert into Persons(name, gender) value('aaa', 1)");

MYSQL_RES * res = executeQuery("select * from Persons");
MYSQL_ROW row;
while (row = mysql_fetch_row(res))
{
char * un = row[0];
char * pwd = row[1];
printf("username = %s, password = %s\n", un, pwd);
}

system("pause");
return 0;
}

void executeNonQuery(char * sql)
{
MYSQL * pConn = mysql_init(0);
if (!mysql_real_connect(pConn, "localhost", "root", "root", "test", 0, 0, 0)) {
printf("连接失败:%s\n", mysql_error(pConn));
goto endd;
}
if (mysql_query(pConn, "set names gbk")) {
printf("设置gbk失败:%s\n", mysql_error(pConn));
goto endd;
}
if (mysql_query(pConn, sql)) {
printf("查询失败:%s\n", mysql_error(pConn));
goto endd;
}
endd:
mysql_close(pConn);
}

MYSQL_RES * executeQuery(char * sql)
{
MYSQL * pConn = mysql_init(0);
if (!mysql_real_connect(pConn, "localhost", "root", "root", "test", 0, 0, 0)) {
printf("连接失败:%s\n", mysql_error(pConn));
return "";
}
if (mysql_query(pConn, "set names gbk")) {
printf("设置gbk失败:%s\n", mysql_error(pConn));
return "";
}
if (mysql_query(pConn, sql)) {
printf("查询失败:%s\n", mysql_error(pConn));
return "";
}

MYSQL_RES * result = mysql_store_result(pConn);
mysql_close(pConn);
return result;
}

表单数据

1
2
3
4
5
char sql[1024]={0}; // 把用户输入的字符串动态拼接生成sql语句
char userName, hexUserName[1024]={0}; // 把可能含有特殊符号的字符串进行16进制转换
mysql_hex_string(hexUserName, userName, strlen(userName)); // 转换成安全的16进制
sprintf(sql, "Insert into T_Users(UserName,Password) values(0x%s,'%s')", hexUserName,password);
// 可以直接识别命令里面0x开头的16进制数据,并自动转换成原来的字符串(数字 0,不是 英文o)
1
2
3
int age;
cgiFormInteger("age", &age, 100); // 获取整型表单。注意,是地址!最后一个是失败后的age默认值
// 例如,当 "age=aaa" 时,取不到整型的值,所以 age = 100
1
2
3
4
5
6
7
int len; // 用来决定需要申请的长度
char *txt;
TMPL_varlist *varlist = 0;
cgiFormStringSpaceNeeded("txt1",&len); // 用于获取可能会非常长的值的长度
txt = (char *)malloc(len+1); // 动态申请内存
cgiFormString("txt1",txt,len);
free(txt); // 最后别忘了 free
1
2
if(cgiFormCheckboxSingle("agree")!=cgiFormSuccess) // 多选框是否选中
<select><option>xxx</option></select> 和 "radio" 都用 cgiFormString 来获取是否输入

文件操作

HTML文件

1
2
3
<form method="post" enctype="multipart/form-data"> // 必须这样写
<input type="file" name="f1">
</form>

文件参数

  • cgiFormFileName:上传的文件名
  • cgiFormFileSize:文件大小(单位为B)
  • cgiFormFileRead:读取上传文件
  • cgiFormFileClose:关闭上传的文件

判断有没有上传文件

1
if(cgiFormFileName("file1",uploadfilename,sizeof(uploadfilename))!=cgiFormSuccess)

获取扩展名

1
2
3
4
_splitpath(uploadfilename,NULL,NULL,NULL,fileExt); // <stdlib.h>中
参数:全路径指针,盘符(带冒号),路径(纯目录无盘符,如 /aaa/),文件名(不包含后缀名),后缀名(带点)
文件格式:d:/aaa/bbb.jpg
if(stricmp(fileExt,".jpg")!=0&&stricmp(fileExt,".png"))…… // stricmp 为不区分大小写的比较

获取文件大小

1
2
cgiFormFileSize("file1",&filesize);
if(filesize>1024*1024)…… // 1024 * 1024 = 1 M

总的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
cgiFilePtr file1; // 声明类似文件指针的变量,网络文件指针
//读取本地文件,用于写入,本地文件指针
FILE *fileLocal = fopen(uploadfilename,"wb"); // C语言读取文件,wb是二进制模式,默认是用文本
char buffer[1024]; // 每次最多1024kb
int lenRead; // 实际读的字节数
cgiFormFileOpen("file1",&file1);
while(cgiFormFileRead(file1,buffer,sizeof(buffer),&lenRead)==cgiFormSuccess)
{
fwrite(buffer,lenRead,1,fileLocal); // buffer[] 的前 lenRead 字节写入到 fileLocal 中
}
fclose(fileLocal);
cgiFormFileClose(file1);
printFileUploadMsg("上传成功");

如鹏网《C语言也能干大事》学习笔记