说到 C 语言版本的程序,首先要解决的问题就是兼容性. 作为 20 年开发有 10 多年是在服务端的程序员,我深刻地感受到服务端平台的两极分化之严重,linux 派对 windows 那是超级的不屑一顾:那都是没技术的人才用的,没能力维护 linux 的人才用 windows. 与此同时 windows 派对 linux 也是嗤之以鼻,我曾经的一位经理就时常不屑对我说,我就不信那几个人写的东西能比公司写的好. 奇怪的这两派其实都很能挣钱,BAT 什么的都用 linux 我们就不说了,但股票期货交易这样重要的而且性能要求一样很高的行业内几乎一水的 windows + sql server 恐怕大家就不知道了吧. 所以我真不太同意 linux 性能就比 windows 高的说法.

我觉得形成这种说法的很重要的一个因素是很多高性能的软件没有 windows 版本,比如 nginx 长期不推荐在 windows 下使用, redis 下的 windows 版本居然是微软自己拿过来修改过才能用的.到底真相如何我们就不讨论了,单就为什么这些软件没有 windows 版本,我觉得一个很重要的原因是 C/C++ 语言在两种平台下的兼容性问题.开源界现在大量的用 gcc,而 gcc 的语法现在和 vc 的语法差别是越来越大,我过去经常在 pc 中引用开源代码,有些代码花上一整天的没法在 vc 中编译通过(印象中最好编译的是 apache 的代码).我个人觉得既然开源了,还是应该考虑一下 vc 的兼容性(当然了个人时间是有限的,我写的很多东西也都没有考虑,基本上手上的平台下能编译过去也就算了...).

这种兼容性体现在很多方面,第一步选择 ide (或者称不上 ide 的开发工具) 时基本上都会要求引入库文件. linux 下是 so 或者 a 文件,这里就不说了,单只讨论 windows 下的就有很多区别. 传统 vc 下是要引入 lib 文件,而现在有大量基于 gcc 的多种开发工具,它们要引入的是 a 文件,它们是不通用的(小提示:有些版本的 gcc 能使用 lib 文件).所以如果是拿一个 vc 的示例,那么在 gcc 系的开发工具中是用不了的. 我的解决办法是不用 socket 的库文件! 初学者还没什么,有经验的同学们又要炸锅了:可能吗! 没什么的可能的,前面已经说了这些 socket 函数是操作系统提供的,与开发语言无关,我们其实可以直接使用操作系统的功能,这种"直接使用"也没什么稀奇的就是直接调用 dll  文件罢了,delphi 的所有 socket 都是这样使用的.具体的调用方法就是直接调用 dll 中的函数指针,这在所有的 windows api 开发书籍中都会讲到,一点也不稀奇.本质上各个编译器最后也是要这样调用的,只不过它们按照传统把这种操作弄到了库文件中了而已.

我先上代码,大家先别急着看,我后面会讲解,其实也都挺简单的.

(文件名 socketplus.c)

  1. //一个方便测试 socket 程序的小文件,省得老是找 lib a 文件
  2. //目前是 gcc 专用,如果 vc 要用另外弄一个好了,不要在这上面弄条件编译
  3.  
  4. #ifndef _SOCKET_PLUS_C_
  5. #define _SOCKET_PLUS_C_
  6.  
  7. #include <stdio.h>
  8. #include <windows.h>
  9. #include <time.h>
  10. #include <winsock.h>
  11. //#include <>
  12.  
  13. #include "lstring.c"
  14.  
  15. //#pragma comment (lib,"*.lib")
  16. //#pragma comment (lib,"libwsock32.a")
  17. //#pragma comment (lib,"libwsock32.a")
  18.  
  19. // 系统错误信息提示
  20. void PrintError(DWORD last_err);
  21.  
  22. //--------------------------------------------------
  23. //直接引入的 dll 函数
  24.  
  25. //SOCKET PASCAL socket(int,int,int);
  26. //char * (*fun1)(char * p1,char * p2);
  27. #ifndef _MSC_VER
  28. //很多同学不会写函数指针声明//函数指针的写法是,先写正常的函数声明,然后将函数名加上括号,然后在函数名前再加上*号即可!!!
  29. SOCKET PASCAL (*_socket)(int,int,int);
  30. //SOCKET (PASCAL *_socket)(int,int,int); //vc 要这样写,vc6,vc2010 都是如此 //就是将 PASCAL 或者 stdcall 放到函数名的括号中
  31. int PASCAL (*_WSAStartup)(WORD,LPWSADATA);
  32. unsigned long PASCAL (*_inet_addr)(const char*);
  33. u_short PASCAL (*_htons)(u_short);
  34. int PASCAL (*_connect)(SOCKET,const struct sockaddr*,int);
  35. int PASCAL (*_WSAGetLastError)(void);
  36. int PASCAL (*_send)(SOCKET,const char*,int,int);
  37. int PASCAL (*_recv)(SOCKET,char*,int,int);
  38. int PASCAL (*_select)(int nfds,fd_set*,fd_set*,fd_set*,const struct timeval*);
  39. struct hostent * PASCAL (*_gethostbyname)(const char*);
  40. #endif
  41.  
  42. #ifdef _MSC_VER
  43. SOCKET (PASCAL *_socket)(int,int,int); //vc 要这样写,vc6,vc2010 都是如此//就是将 PASCAL 或者 stdcall 放到函数名的括号中
  44. int (PASCAL *_WSAStartup)(WORD,LPWSADATA);
  45. unsigned long (PASCAL*_inet_addr)(const char*);
  46. u_short (PASCAL*_htons)(u_short);
  47. int (PASCAL *_connect)(SOCKET,const struct sockaddr*,int);
  48. int (PASCAL *_WSAGetLastError)(void);
  49. int (PASCAL *_send)(SOCKET,const char*,int,int);
  50. int (PASCAL *_recv)(SOCKET,char*,int,int);
  51. int (PASCAL *_select)(int nfds,fd_set*,fd_set*,fd_set*,const struct timeval*);
  52. struct hostent * (PASCAL *_gethostbyname)(const char*);
  53. #endif
  54.  
  55. //--------------------------------------------------
  56.  
  57. int CreateTcpClient()
  58. {
  59. //LoadLibrary("wsock32.dll");
  60. return _socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  61. }
  62.  
  63. int InitWinSocket()
  64. {
  65. WSADATA wData;
  66.  
  67. //Result := WSAStartup(MakeWord(2, 2), wData) = 0;
  68. return _WSAStartup(MAKEWORD(, ), &wData);
  69.  
  70. }
  71.  
  72. int ConnectIP(SOCKET so, char * ip, int port)
  73. {
  74. //sock: TSocket;
  75. //SockAddr: TSockAddr;
  76. //struct sockaddr SockAddr;
  77. struct sockaddr_in SockAddr;
  78.  
  79. int err;
  80. int Result = ;
  81.  
  82. memset(&SockAddr, , sizeof(SockAddr));
  83.  
  84. SockAddr.sin_family = AF_INET;
  85. SockAddr.sin_port = _htons(port);
  86. SockAddr.sin_addr.s_addr = _inet_addr(ip);
  87.  
  88. //if (_connect(so, (struct sockaddr *)(&SockAddr), sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
  89. if (_connect(so, &SockAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) //其实是不用转换的
  90. {
  91. PrintError();
  92. err = _WSAGetLastError(); //其实这个和 GetLastError 是一样的
  93. //ShowMessageFmt('connect socket error,[%d]', [WSAGetLastError]);
  94. MessageBox(, "connect socket error", "", );
  95. Result = ;
  96.  
  97. //int err = _WSAGetLastError();//如果前面调用了别的 api 这里是取到 0 的,而不是错误信息码
  98.  
  99. PrintError(err);
  100.  
  101. if (INVALID_SOCKET == so) printf("connect error:INVALID_SOCKET\r\n");
  102.  
  103. return ;
  104. }
  105.  
  106. return ;
  107.  
  108. }//
  109.  
  110. //clq 这是我新加的函数,目的是可以根据域名来访问,并且原来的代码只能访问局域网
  111.  
  112. int ConnectHost(SOCKET so, char * host, int port)
  113. {
  114. const char * address = host;
  115. int is_connect = ;
  116. int err = ;
  117.  
  118. // Create an address structure and clear it
  119. struct sockaddr_in addr;
  120. memset(&addr, , sizeof(addr));
  121.  
  122. // Fill in the address if possible//先尝试当做IP来解析
  123. addr.sin_family = AF_INET;
  124. addr.sin_addr.s_addr = _inet_addr(address);
  125.  
  126. // Was the string a valid IP address?//如果不是IP就当做域名来解析
  127. if (addr.sin_addr.s_addr == -)
  128. {
  129. // No, so get the actual IP address of the host name specified
  130. struct hostent *pHost;
  131. pHost = _gethostbyname(address);
  132.  
  133. if (pHost != NULL)
  134. {
  135. if (pHost->h_addr == NULL)
  136. return ;//false;
  137.  
  138. addr.sin_addr.s_addr = ((struct in_addr *)pHost->h_addr)->s_addr;
  139. }
  140. else
  141. return ;//false;
  142. }
  143.  
  144. addr.sin_port = _htons(port);
  145.  
  146. //返回:-1 连接失败;0 连接成功
  147. if (_connect(so, (struct sockaddr *)&addr, sizeof(addr)) == )
  148. {
  149. is_connect = ;//true;
  150. }
  151. else
  152. {
  153. is_connect = ;//false;
  154. //连接失败
  155. //printfd3("WSAGetLastError:%d, WSAEWOULDBLOCK:%d\r\n", WSAGetLastError()-WSABASEERR, WSAEWOULDBLOCK-WSABASEERR);
  156.  
  157. PrintError();
  158. err = _WSAGetLastError(); //其实这个和 GetLastError 是一样的
  159. //ShowMessageFmt('connect socket error,[%d]', [WSAGetLastError]);
  160. MessageBox(, "connect socket error", "", );
  161. is_connect = ;
  162.  
  163. //int err = _WSAGetLastError();//如果前面调用了别的 api 这里是取到 0 的,而不是错误信息码
  164.  
  165. PrintError(err);
  166.  
  167. if (INVALID_SOCKET == so) printf("connect error:INVALID_SOCKET\r\n");
  168. }
  169.  
  170. return is_connect;
  171. }//
  172.  
  173. #ifndef faveLoadFunction
  174. #define faveLoadFunction
  175. FARPROC WINAPI LoadFunction(HINSTANCE h, LPCSTR fun_name)
  176. {
  177. FARPROC WINAPI r = ;
  178.  
  179. r = GetProcAddress(h, fun_name);
  180.  
  181. if (r == ) printf("load function %s error\r\n", fun_name);
  182. else printf("load function %s ok\r\n", fun_name);
  183.  
  184. return r;
  185. }//
  186. #endif
  187.  
  188. void LoadFunctions_Socket()
  189. {
  190. //HINSTANCE hs = LoadLibrary("wsock32.dll"); //根据不同的编译环境,有可能要从 LoadLibrary 改成 LoadLibraryA
  191. HINSTANCE hs = LoadLibraryA("wsock32.dll"); //根据不同的编译环境,有可能要从 LoadLibrary 改成 LoadLibraryA
  192.  
  193. if (hs == ) printf("load wsock32.dll error\r\n", hs);
  194. else printf("load wsock32.dll ok\r\n", hs);
  195.  
  196. _socket = GetProcAddress(hs, "socket");
  197.  
  198. printf("_socket:%d\r\n", _socket);
  199. if (_socket == ) printf("load _socket error\r\n", hs);
  200.  
  201. //--------------------------------------------------
  202. //直接装载各个 dll 函数
  203. _socket = LoadFunction(hs, "socket");
  204. _WSAStartup = LoadFunction(hs, "WSAStartup");
  205. _inet_addr = LoadFunction(hs, "inet_addr");
  206. _htons = LoadFunction(hs, "htons");
  207. _connect = LoadFunction(hs, "connect");
  208. _WSAGetLastError = LoadFunction(hs, "WSAGetLastError");
  209. _send = LoadFunction(hs, "send");
  210. _recv = LoadFunction(hs, "recv");
  211. _select = LoadFunction(hs, "select");
  212. _gethostbyname = LoadFunction(hs, "gethostbyname");
  213.  
  214. }
  215.  
  216. // 系统错误信息提示
  217. void PrintError(DWORD last_err)
  218. {
  219. //进行出错。
  220. //if (!CreateDirectory(_T("c:\\"),0))
  221. {
  222. char buf[];//char buf[128];
  223. LPVOID lpMsgBuf;
  224. DWORD dw;
  225.  
  226. memset(&buf, , sizeof(buf));
  227.  
  228. dw = last_err;//GetLastError();
  229.  
  230. if (dw == ) dw = GetLastError(); //实际上是可以代替 WSAGetLastError 的
  231.  
  232. //dw = 5;//10035;//6000054;
  233.  
  234. if (dw == ) return;
  235.  
  236. FormatMessage (
  237. FORMAT_MESSAGE_FROM_SYSTEM,//FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, //FORMAT_MESSAGE_ALLOCATE_BUFFER 是指要分配内存
  238. NULL,
  239. dw,
  240. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  241. (LPTSTR) &buf,//(LPTSTR) &lpMsgBuf,
  242. ,//0, //因为是自己分配的内存,所以要指出分配了多大
  243. NULL );
  244.  
  245. printf("PrintError(出错码=%d):%s\r\n", dw, buf); //奇怪,这里就是不对//是倒数第 2 个 参数的问题
  246.  
  247. FormatMessage (
  248. FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, //FORMAT_MESSAGE_ALLOCATE_BUFFER 是指要分配内存
  249. NULL,
  250. dw,
  251. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  252. (LPTSTR) &lpMsgBuf,
  253. , NULL );
  254.  
  255. wsprintf(buf,
  256. "出错信息 (出错码=%d): %s",
  257. dw, lpMsgBuf);
  258.  
  259. printf("PrintError(出错码=%d):%s\r\n", dw, lpMsgBuf);
  260. //printf("PrintError(出错码=%d):%s\r\n", dw, &buf); //奇怪,这个是不对的
  261. printf("PrintError(出错码=%d):%s\r\n", dw, buf);
  262.  
  263. LocalFree(lpMsgBuf);
  264.  
  265. //输出提示
  266. //OutputDebugString(szBuf);
  267. }
  268. }//
  269.  
  270. //尚未精测试,可能有误
  271. int SendBuf(SOCKET so, char * s, int len)
  272. {
  273. int r = ;
  274. int count = ;
  275. char * p = s;
  276.  
  277. //r := 0;
  278. int Result = ;
  279. //count := 0;
  280. //p := @s[1];
  281.  
  282. while (Result<len)
  283. {
  284. r = _send(so, p, len - Result, );
  285. if (r > )
  286. {
  287. Result = Result + r;
  288. p = p + r;
  289.  
  290. };
  291.  
  292. count++;
  293.  
  294. if (count>) //超过多少次就不发送了
  295. {
  296. MessageBox(, "send error", "", );
  297.  
  298. return Result;
  299. }
  300.  
  301. }
  302.  
  303. return Result;
  304.  
  305. }//
  306.  
  307. //注意,返回的字符串要自己释放
  308. //lstring RecvBuf(SOCKET so)
  309. //算了,还是传可自动释放的字符串进去方便点
  310. //void RecvBuf(SOCKET so, lstring * buf)//用这个格式也可以,不过与其他语言不通用
  311. lstring * RecvBuf(SOCKET so, struct MemPool * pool)
  312. {
  313. char buf[+];
  314. int r = ;
  315. //lstring s = String("");
  316. //lstring * s = NewString("", _buf->pool);
  317. //CheckPString(_buf);
  318. lstring * s = NewString("", pool);
  319.  
  320. memset(&buf, , sizeof(buf));
  321.  
  322. r = _recv(so, buf, sizeof(buf)-, ); //留下一个 #0 结尾
  323. if (r > )
  324. {
  325. //SetLength(s, r);
  326. //Move(buf, s[1], r);
  327. //s.Append(&s, StringConst(buf, r));
  328. LString_AppendCString(s, buf, r);
  329. }
  330.  
  331. if (r == ) //一般都是断开了
  332. {
  333. MessageBox(, "recv error.[socket close]", "", );
  334.  
  335. //return String(""); //不应该再生成一个新的
  336. return s;
  337. }
  338.  
  339. return s;
  340.  
  341. }//
  342.  
  343. //是否可读取
  344. int SelectRead(SOCKET so)
  345. {
  346. fd_set fd_read; //fd_read:TFDSet;
  347. struct timeval timeout; // : TTimeVal;
  348.  
  349. int Result = ;
  350.  
  351. FD_ZERO( &fd_read );
  352. FD_SET(so, &fd_read ); //个数受限于 FD_SETSIZE
  353.  
  354. timeout.tv_sec = ; //秒
  355. timeout.tv_usec = ; //毫秒
  356. timeout.tv_usec = ; //毫秒
  357.  
  358. //linux 第一个参数一定要赋值
  359. ////int r = ::select(socket_handle+1, &fd_read, NULL, NULL, &l_timeout);
  360.  
  361. //if select( 0, &fd_read, nil, nil, &timeout ) > 0 then //至少有1个等待Accept的connection
  362. if (_select( so+, &fd_read, NULL, NULL, &timeout ) > ) //至少有1个等待Accept的connection
  363. Result = ;
  364.  
  365. return Result;
  366.  
  367. }//
  368.  
  369. #endif

当然,我这些代码倒也不是为了写示例方便大家测试,而是因为工作的关系我需要在多个 ide 和多种编译器中切换,早就写好用来测试网络应用的(主要是xmpp协议,所以以后我们也顺便介绍一下 xmpp 协议的一些简单实现).

其中有以下几点需要说明一下:
1.函数需要从 dll 中取出其地址,在代码中实现为 LoadFunctions_Socket();
2.LoadLibraryA/LoadLibrary 函数可以理解为将一个 dll 中的代码读取到程序的内存中;
3.GetProcAddress 函数可以理解为找到一个函数的地址;
4.不能直接使用原始的 socket 函数名,要使用时,在前面加一个划线;
5.只引入了我用到的少数几个 socket 函数,有需要的网友可以自己引入:方法是先声明一个同原型的函数指针,然后加入到 LoadFunctions_Socket() 中就可以了.
6.代码主要工作在 windows 版本的 gcc, vc6 和 vc2010 下也做过测试. 随着代码的增加 vc 下如果不能运行,大家可以自己改下函数指针的声明和 api 函数的版本(换下 a 和 w 的版本). 至于 a/w 版本是什么意思,不懂的同学我们以后再说明吧.
7.我直接 include 了 c 的文件,方便在各个开发工具中切换,不喜欢这种方式的同学请自己改成 h 文件的方式吧.

我主要的工作环境是 cfree,原因主要是 vc 产生的临时文件实在是太大了,设置不产生临时文件又会产生别的问题,而用 dev c++ 这些传统的 gcc 环境的话,代码提示又是一个问题. 找了很久我最终选用了 cfree 软件,不过要说明的是 cfree 是收费的,但费用不足百元,所以虽然网上有很多注册码可以用我还是推荐大家注册一下.遗憾的是,国内的共享环境之恶劣导致我付费时很是花了几天时候,最终是找作者要到了他的淘宝账号直接转的钱,作者一度以为我是骗子让我去联系第三方的注册网站,我觉得那个第三方的注册网站更象骗子,所以坚持直接转给他.之所以我认为第三方注册机构更象骗子是因为我也写过共享软件 ...

cfree 对我来说有以下吸引力的优点:
1.没有太大的临时文件;
2.代码提示还不错;
3.不需要建立工程文件;
4.注册费用低,我是它的正版用户;
5.通过配置可以把编辑器界面弄得象我喜欢的 vscode;
6.我有一个 32 位的 windows 2003 工作环境,那里用不了 vscode,也不用了现在最新的 vc;
7.作为 gcc 系,几乎不需要配置编译环境.

这里要提一下开源的 codelite,也是不错的软件,代码提示也做得很好,但生成的临时文件大了点,而且需要配置编译环境(对初学者很致命),如果没有 cfree 我就用它了.

另外一定要说的是它们的调试都很差,如果有无法理解的代码,请打开您的 VC ... 不过我现在调试用的是我能找到的最简便的网友简化版的非官方版本 BCB2009 (其实人家叫 CodeGear C++ Builder 2009,bcb 的称呼只是历史原因),因为这个版本安装非常方便,要不我就用 bcb6 了.原版 bcb2009 是否方便我就不清楚了. 找不到简化版本的同学用最常见的 vc6 调试就行了,一样好用.

好了,下面给出测试的 demo 代码. c 语言中还有些要注意的字节对齐等问题也来不及说了,我们下篇再说吧.

  1. gSo = CreateTcpClient();
  2. r = ConnectHost(gSo, "newbt.net", );
  3.  
  4. if (r == ) printf("连接成功!\r\n");
  5.  
  6. s = NewString("EHLO\r\n", m);
  7.  
  8. SendBuf(gSo, s->str, s->len);
  9.  
  10. printf(s->str);
  11. s->Append(s, s);
  12. printf(s->str);
  13.  
  14. s->AppendConst(s, "中文\r\n");
  15.  
  16. printf(s->str);
  17.  
  18. rs = RecvBuf(gSo, m); //注意这个并不只是收取一行
  19.  
  20. printf("\r\nRecvBuf:\r\n");
  21. printf(rs->str);
  22.  
  23. rs = RecvBuf(gSo, m); //注意这个并不只是收取一行
  24. printf("\r\nRecvBuf:\r\n");
  25. printf(rs->str);

这是通讯过程,代码并不复杂,大家可以看到简单封装后并不复杂,所以按自己顺手的封装一下很有必要.

以下是完整代码:

(文件名 socket_test1.c)

  1. #include <stdio.h>
  2. #include <windows.h>
  3. #include <time.h>
  4. #include <winsock.h>
  5.  
  6. #include "lstring.c"
  7. #include "socketplus.c"
  8. #include "lstring_functions.c"
  9.  
  10. //vc 下要有可能要加 lib
  11. //#pragma comment (lib,"*.lib")
  12. //#pragma comment (lib,"libwsock32.a")
  13. //#pragma comment (lib,"libwsock32.a")
  14.  
  15. //SOCKET gSo = 0;
  16. SOCKET gSo = -;
  17.  
  18. void main()
  19. {
  20. int r;
  21. mempool mem, * m;
  22. lstring * s;
  23. lstring * rs;
  24.  
  25. //--------------------------------------------------
  26.  
  27. mem = makemem(); m = &mem; //内存池,重要
  28.  
  29. //--------------------------------------------------
  30. //直接装载各个 dll 函数
  31. LoadFunctions_Socket();
  32.  
  33. InitWinSocket(); //初始化 socket, windows 下一定要有
  34.  
  35. gSo = CreateTcpClient();
  36. r = ConnectHost(gSo, "newbt.net", );
  37.  
  38. if (r == ) printf("连接成功!\r\n");
  39.  
  40. s = NewString("EHLO\r\n", m);
  41.  
  42. SendBuf(gSo, s->str, s->len);
  43.  
  44. printf(s->str);
  45. s->Append(s, s);
  46. printf(s->str);
  47.  
  48. s->AppendConst(s, "中文\r\n");
  49.  
  50. printf(s->str);
  51.  
  52. rs = RecvBuf(gSo, m); //注意这个并不只是收取一行
  53.  
  54. printf("\r\nRecvBuf:\r\n");
  55. printf(rs->str);
  56.  
  57. rs = RecvBuf(gSo, m); //注意这个并不只是收取一行
  58. printf("\r\nRecvBuf:\r\n");
  59. printf(rs->str);
  60.  
  61. //--------------------------------------------------
  62.  
  63. Pool_Free(&mem); //释放内存池
  64.  
  65. printf("gMallocCount:%d \r\n", gMallocCount); //看看有没有内存泄漏//简单的检测而已
  66.  
  67. //--------------------------------------------------
  68.  
  69. getch(); //getch().不过在VC中好象要用getch(),必须在头文件中加上<conio.h>
  70.  
  71. }

除了通讯 demo 部分外,其他代码大家都不用细看,都是临时的辅助封装函数而已,用到生产环境中后果我可不负责(虽然我认为改一改也是可以用的).就当是抛砖引玉吧.

代码里其实还用到了一个很重要的字符串类,不过也是来不及解说了,先给出代码如下:
(文件名为 lstring.c 和 lstring_functions.c,本来我是要放到 github 上的,不过估计也是没时间维护了,所以直接贴一下吧)

  1. //没办法还是得另定义一个字符串
  2.  
  3. #ifndef _L_STRING_C_
  4. #define _L_STRING_C_
  5.  
  6. #include <stdio.h>
  7. #include <malloc.h> //有些编译,如果 malloc.h 在后面的话会报 malloc 函数冲突,解决办法很简单,把含有 malloc 的头文件放前面,好让我们的 malloc 定义能覆盖它就可以了
  8. #include <string.h>
  9. //#include <time.h>
  10. //#include <winsock.h>
  11. #include <windows.h>
  12. #include <time.h>
  13. //#include <crt/eh.h> //据说 MinGW终于支持使用seh实现c++ eh了
  14. //https://sourceforge.net/p/mingw-w64/mingw-w64/ci/18a7e88bcbe8bc0de4e07dac934ebf0653c4da7c/tree/mingw-w64-headers/crt/eh.h
  15.  
  16. int gMallocCount = ;
  17.  
  18. //简单的内存泄漏检测
  19. void * malloc_v2(size_t size)
  20. {
  21. gMallocCount++;
  22. return malloc(size);
  23. }
  24.  
  25. void * free_v2(void * p)
  26. {
  27. gMallocCount--;
  28. free(p);
  29. }
  30.  
  31. #define malloc malloc_v2
  32. #define free free_v2
  33.  
  34. typedef struct LString _LString; //相互引用的提前声明好象必须用 typedef
  35.  
  36. //内存池中的一项//算了, string 的自动释放就很复杂了,还是专注于 string 的吧
  37. struct MemPool_Item{
  38. //void * data; //要释放的数据,可以是不同类型的
  39. _LString * data; //要释放的数据,可以是不同类型的
  40.  
  41. //void (*FreeFunc)(struct MemPool_Item * item); //对应节点的释放函数//太复杂,就当做原始的 free 函数释放就行了
  42.  
  43. struct MemPool_Item * next;
  44. };
  45.  
  46. //内存池,用于释放一次函数过程中分配的内存
  47. struct MemPool{
  48.  
  49. struct MemPool_Item * Items;
  50. int Count;
  51. char * str;
  52. byte _const; //是否是只读的,如果是只读的就是从数组中构造的,不要释放它
  53. int id; //只是为调试释放而已
  54.  
  55. };
  56.  
  57. struct LString{
  58.  
  59. char * _init; //只是用来判断是否进行了初始化,不可靠的简单判断,为 0 就是初始化了
  60.  
  61. int len;
  62. char * str;
  63. byte _const; //是否是只读的,如果是只读的就是从数组中构造的,不要释放它
  64. byte _malloc; //这个结构是不是 malloc 生成的,如果是还要 free 掉结构体本身
  65. struct MemPool * pool; //字符串所在的函数体自动释放内存池,如果是 NULL 就是要手工释放的
  66. //在操作字符串的临时函数中可以使用它
  67.  
  68. void (*Append)(struct LString * s, struct LString * add);
  69. void (*AppendConst)(struct LString * s, char * add);
  70.  
  71. };
  72.  
  73. #define mempool struct MemPool
  74.  
  75. //函数体临时使用字符串时
  76. //#define USEPOOL struct MemPool _function_mem_ = makemem();
  77.  
  78. //#define ENDUSEPOOL Pool_Free(&_function_mem_);
  79.  
  80. //输出打印的级别
  81.  
  82. //最普通的错误信息
  83. #define printf_err printf
  84. //最低级别的打印
  85. #define printf_err1
  86. //#define printf_err1 printf
  87.  
  88. mempool makemem()
  89. {
  90. mempool mem;
  91. memset(&mem, , sizeof(mem));
  92.  
  93. srand((unsigned) time(NULL)); //用时间做种,每次产生随机数不一样
  94. mem.id = rand(); //number = rand() % 101; //产生0-100的随机数
  95.  
  96. return mem;
  97. }
  98.  
  99. mempool * newmempool()
  100. {
  101. mempool * pmem;
  102. pmem = malloc(sizeof(struct MemPool));
  103. memset(pmem, , sizeof(struct MemPool));
  104.  
  105. srand((unsigned) time(NULL)); //用时间做种,每次产生随机数不一样
  106. pmem->id = rand(); //number = rand() % 101; //产生0-100的随机数
  107.  
  108. return pmem;
  109. }
  110.  
  111. //太复杂,就当做一个字符串池,用在一个函数体结束时自动释放这个过程中产生的所有临时字符串,类似于 php 的自动释放原理
  112. //加入一个节点, pool 并不分配内存,只是把大家加到一个链表中统一释放而已 //函数参数的定义的函数指针是一样的
  113. void Pool_AddItem(struct MemPool * pool, void * p)
  114. {
  115. struct MemPool_Item * item = NULL;
  116. _LString * s = NULL;
  117.  
  118. if (pool == NULL) return;
  119.  
  120. //--------------------------------------------------
  121. //简单的验证
  122.  
  123. s = p;//item->data;
  124.  
  125. if (s->_init != NULL) //简单的初始化检测
  126. {
  127. printf("Pool_AddItem: error 未初始化的字符串指针!!!\r\n");
  128. }
  129.  
  130. //--------------------------------------------------
  131.  
  132. item = malloc(sizeof(struct MemPool_Item));
  133. memset(item, , sizeof(struct MemPool_Item));
  134.  
  135. //item->FreeFunc = FreeFunc;
  136. item->data = p;
  137.  
  138. //下面两步是替换掉头节点
  139. item->next = pool->Items;
  140. pool->Items = item;
  141.  
  142. pool->Count++;
  143.  
  144. }//
  145.  
  146. //释放一大片,可以做些简单的检测
  147. void Pool_Free(struct MemPool * pool)
  148. {
  149. struct MemPool_Item * item = NULL;
  150. int i = ;
  151.  
  152. _LString * s = NULL;
  153.  
  154. for(i=; i<pool->Count; i++)
  155. {
  156. printf_err1("Pool_Free:%d, %d\r\n", pool->Count, i);
  157. item = pool->Items;
  158. //item->FreeFunc(item->data);
  159. //item->FreeFunc(item); //这样更清晰一点
  160.  
  161. s = item->data;
  162.  
  163. if (s->_init != NULL) //简单的初始化检测
  164. {
  165. printf("Pool_Free: error 未初始化的字符串指针!!!\r\n");
  166. }
  167.  
  168. //free(item->data);
  169. free(s->str);
  170.  
  171. if (s->_malloc == ) //结构体本身也要释放的话
  172. free(s);
  173.  
  174. pool->Items = pool->Items->next; //向下移动一个指针位置
  175. free(item); //节点自己的内存也要释放
  176. }
  177.  
  178. pool->Count = ;
  179.  
  180. }
  181.  
  182. //自动释放
  183. //void autofree(struct MemPool * pool, void * p)
  184. //{
  185. // Pool_AddItem(pool, p);
  186. //}
  187.  
  188. void freemempool(mempool * pool)
  189. {
  190.  
  191. Pool_Free(pool);
  192.  
  193. free(pool);
  194. }
  195.  
  196. #define freemem Pool_Free
  197.  
  198. struct LStringRef{
  199.  
  200. //const struct LBuf * buf; //这个是保存内存内容的地方,不应该变 //本意是让这个指针值固定,但这样导致里面的值也变不了
  201. struct LString * buf; //这个是保存内存内容的地方,不应该变 //C 语言的特点,为了在传递参数时不使用指针,只能是把指针放到成员中
  202.  
  203. void (*Append)(struct LString * s, struct LString * add);
  204. //int (*AppendConst)(struct LString * s, const char * add);
  205.  
  206. };
  207.  
  208. #define lstring struct LString
  209.  
  210. #define PLString struct LString *
  211.  
  212. #define stringref struct LStringRef
  213.  
  214. //#define GetStr s.str
  215.  
  216. //下面这几个宏可是可以用,不过太容易冲突了,最好是逻辑清晰度要求很高的地方才用
  217. #define GetStr buf->str
  218. #define GetLen buf->len
  219.  
  220. //#define str() buf->str
  221. //#define len() buf->len
  222.  
  223. #define str__ buf->str
  224. #define len__ buf->len
  225.  
  226. //为了能自动释放,只能是指针,结构体在参数传递时会丢失字段值,所以完全的模仿 C++ 是不可能的
  227.  
  228. //绑定各个成员函数
  229. void BindStringFunctions(lstring * s);
  230. //lstring String(char * str);
  231.  
  232. //自动释放//只释放字符串
  233. void autofree_s(struct MemPool * pool, struct LString * s)
  234. {
  235. if (s == NULL) return;
  236.  
  237. s->pool = pool; //加上这个标志,这样根据这个 s 操作出来的 string 都可以通过它自动释放了
  238.  
  239. //Pool_AddItem(pool, p);
  240. //Pool_AddItem(pool, s->str);
  241. Pool_AddItem(pool, s);
  242. }
  243.  
  244. //自动释放//不只释放字符串,连指针一起释放
  245. void autofree_pstring(struct MemPool * pool, struct LString * s)
  246. {
  247. if (s == NULL) return;
  248.  
  249. s->pool = pool; //加上这个标志,这样根据这个 s 操作出来的 string 都可以通过它自动释放了
  250.  
  251. Pool_AddItem(pool, s);
  252. //Pool_AddItem(pool, s->str);
  253. }
  254.  
  255. //
  256. ////分配并返回一个字符串
  257. //stringref MakeString(char * str)
  258. //{
  259. // stringref s;
  260. //
  261. // s.buf = malloc(sizeof(struct LString));
  262. // memset(s.buf, 0, sizeof(struct LString));
  263. //
  264. // s.buf->_const = 0;
  265. // s.buf->len = strlen(str);
  266. // s.buf->str = NULL;
  267. //
  268. // if (str != NULL)
  269. // {
  270. // s.buf->str = malloc(s.buf->len + 1); //还要留最后 \0 的位置
  271. // memset(s.buf->str, 0, s.buf->len + 1);
  272. //
  273. // strcpy(s.buf->str, str);
  274. // }
  275. //
  276. // //绑定各个成员函数
  277. // //BindStringFunctions(&s);
  278. //
  279. // return s;
  280. //
  281. //}//
  282. //
  283.  
  284. ////分配并返回一个字符串指针
  285. //lstring * PString(lstring s)
  286. //{
  287. // //lstring s = String(str);
  288. //
  289. // lstring * p = malloc(sizeof(struct LString));
  290. //
  291. // *p = s;
  292. // p->_malloc = 1; //要释放结构体本身
  293. //
  294. // autofree_pstring(s->pool, p);
  295. //
  296. // return p;
  297. //
  298. //}//
  299. //
  300.  
  301. ////释放 //pfree 是否释放指针本身
  302. //void FreeStringRef(stringref * s, int pfree)
  303. //{
  304. // if (s->buf == NULL || s->buf->str == NULL)
  305. // {
  306. // printf("FreeString() error: string is NULL");
  307. //
  308. // return;
  309. // }
  310. //
  311. // if (s->buf->_const != 0)
  312. // {
  313. // printf("FreeString() error: string is readonly");
  314. //
  315. // //return;
  316. // }
  317. // else
  318. // {
  319. // free(s->buf->str);
  320. // s->buf->str = NULL;
  321. // }
  322. //
  323. // free(s->buf);
  324. //
  325. // if (pfree)
  326. // free(s);
  327. //
  328. //}//
  329.  
  330. void _FreeString(lstring * s, int pfree)
  331. {
  332. if (s == NULL)
  333. {
  334. printf("FreeString() error: lstring * s is NULL");
  335.  
  336. return;
  337. }
  338.  
  339. if (s->str == NULL)
  340. {
  341. printf("FreeString() error: s->str is NULL");
  342.  
  343. return;
  344. }
  345.  
  346. if (s->_const != )
  347. {
  348. printf("FreeString() error: string is readonly");
  349.  
  350. //return;
  351. }
  352. else
  353. {
  354. free(s->str);
  355. s->str = NULL;
  356. }
  357.  
  358. if (pfree) //是否释放指针本身
  359. free(s);
  360.  
  361. }//
  362.  
  363. void FreeString(lstring s)
  364. {
  365. _FreeString(&s, ); //只释放数据
  366. }
  367.  
  368. void FreePString(lstring * s)
  369. {
  370. _FreeString(s, ); //释放数据和指针
  371. }
  372.  
  373. ////释放//给内存池调用的
  374. //void FreeString_ForPool(struct MemPool_Item * poolitem)
  375. //{
  376. // //void * p = poolitem->data;
  377. // lstring * p = poolitem->data;
  378. //
  379. // if (p == NULL) return;
  380. //
  381. // printf("准备释放:%s\r\n", p->str);
  382. //
  383. //
  384. // FreePString(p);
  385. //
  386. //
  387. //}//
  388. //
  389.  
  390. //分配一个内存池释放的
  391. //lstring * StringPool(struct MemPool * pool, char * str)
  392. //{
  393. //
  394. // lstring * s = PString(str);
  395. // Pool_AddItem(pool, s, FreeString_ForPool);
  396. //
  397. // return s;
  398. //}
  399.  
  400. //分配一个内存池释放的
  401. //stringref StringRef(struct MemPool * pool, char * str)
  402. //{
  403. //
  404. // lstring * s = PString(str);
  405. // Pool_AddItem(pool, s, FreeString_ForPool);
  406. //
  407. // return *s;
  408. //}
  409.  
  410. //用结构体就做不了自动释放,所以还是用指针吧,毕竟不是 C++
  411. //lstring String(char * str, struct MemPool * pool)
  412. //{
  413. //
  414. // lstring s;
  415. //
  416. // memset(&s, 0, sizeof(struct LString));
  417. //
  418. // s.len = strlen(str);
  419. // s.str = malloc(s.len+1);
  420. // memset(s.str, 0, s.len+1);
  421. //
  422. // memcpy(s.str, str, s.len);
  423. //
  424. // //绑定各个成员函数
  425. // BindStringFunctions(&s);
  426. //
  427. // autofree_s(pool, s);
  428. //
  429. // return s;
  430. //}
  431.  
  432. //各个常用函数中尽量使用这个函数分配新字符串,因为它生成的可自动释放,避免 autofree 满天飞
  433. //这个应该是基础函数,不要使用其他函数实现
  434. lstring * NewString(char * str, struct MemPool * pool)
  435. {
  436.  
  437. //lstring s = String(str);
  438. lstring * p = malloc(sizeof(struct LString));
  439.  
  440. memset(p, , sizeof(struct LString));
  441.  
  442. p->_malloc = ;
  443.  
  444. p->len = strlen(str);
  445. p->str = malloc(p->len+);
  446. memset(p->str, , p->len+);
  447.  
  448. memcpy(p->str, str, p->len);
  449.  
  450. //绑定各个成员函数
  451. BindStringFunctions(p);
  452.  
  453. autofree_pstring(pool, p);
  454.  
  455. return p;
  456. }
  457.  
  458. //一般用于参数传递
  459. lstring StringCopy(lstring str)
  460. {
  461.  
  462. lstring s;
  463.  
  464. memset(&s, , sizeof(struct LString));
  465.  
  466. s.len = str.len;
  467.  
  468. s.str = malloc(s.len+);
  469. memset(s.str, , s.len+);
  470.  
  471. memcpy(s.str, str.str, str.len);
  472.  
  473. //绑定各个成员函数
  474. BindStringFunctions(&s);
  475.  
  476. return s;
  477. }
  478.  
  479. //复制一个字符串给另外一个内存池,如果对方为 NULL 那么就变成自由的字符串了,不过最好是不要这样做,应当从设计上要求每个字符串生成时都有medh池
  480. lstring * PStringCopyToPool(lstring * s, struct MemPool * pool)
  481. {
  482.  
  483. lstring * p = malloc(sizeof(struct LString));
  484.  
  485. memset(p, , sizeof(struct LString));
  486.  
  487. p->_malloc = ; //结构体本身是分配的内存,也要释放
  488.  
  489. p->len = s->len;
  490.  
  491. p->str = malloc(s->len+);
  492. memset(p->str, , s->len+);
  493.  
  494. memcpy(p->str, s->str, s->len);
  495.  
  496. //绑定各个成员函数
  497. BindStringFunctions(p);
  498.  
  499. //if (autofree == 1) autofree_pstring(s->pool, p);
  500. autofree_pstring(pool, p);
  501.  
  502. return p;
  503. }//
  504.  
  505. //复制一个字符串
  506. //lstring * PStringCopy(lstring * s, int autofree)
  507. lstring * PStringCopy(lstring * s)
  508. {
  509. return PStringCopyToPool(s, s->pool);
  510.  
  511. // lstring * p = malloc(sizeof(struct LString));
  512. //
  513. // memset(p, 0, sizeof(struct LString));
  514. //
  515. // p->_malloc = 1; //结构体本身是分配的内存,也要释放
  516. //
  517. // p->len = s->len;
  518. //
  519. // p->str = malloc(s->len+1);
  520. // memset(p->str, 0, s->len+1);
  521. //
  522. // memcpy(p->str, s->str, s->len);
  523. //
  524. // //绑定各个成员函数
  525. // BindStringFunctions(p);
  526. //
  527. // //if (autofree == 1)
  528. // autofree_pstring(s->pool, p);
  529. //
  530. // return p;
  531. }//
  532.  
  533. //不分配内存,只是将一个缓冲区按 string 方式操作而已,类似 golang 的 bytes ,所以不要释放它
  534. //这个也是基础函数 ,虽然它的内存不用释放,但也还是要传 pool ,以便给生成的子字符串自动释放的机会
  535. lstring StringConst(char * str, int len, struct MemPool * pool)
  536. {
  537. lstring s;
  538.  
  539. //s._const = 0;
  540.  
  541. s._const = ;
  542. s.len = len; //strlen(str);
  543. s.str = str; //malloc(s.len);
  544.  
  545. s.pool = pool;
  546.  
  547. //绑定各个成员函数
  548. BindStringFunctions(&s);
  549.  
  550. return s;
  551.  
  552. }//
  553.  
  554. void LString_Append(lstring * _s, lstring * _add)
  555. {
  556. lstring s = (*_s); //牺牲一点点性能来换语法//golang 就没有 ->
  557. lstring add = (*_add);//牺牲一点点性能来换语法//golang 就没有 ->
  558.  
  559. char * tmp = malloc(s.len + add.len + ); //还要留最后 \0 的位置
  560. memset(tmp, , s.len + add.len + );
  561.  
  562. memcpy(tmp, s.str, s.len);
  563. memcpy(tmp + s.len, add.str, add.len); //这里要注意//加上后半段
  564.  
  565. free(s.str);
  566.  
  567. s.str = tmp;
  568. s.len = s.len + add.len;
  569.  
  570. // char * tmp = malloc(s->len + add->len + 1); //还要留最后 \0 的位置
  571. // memset(tmp, 0, s->len + add->len + 1);
  572. //
  573. // memcpy(tmp, s->str, s->len);
  574. // memcpy(tmp + s->len, add->str, add->len); //这里要注意//加上后半段
  575. //
  576. // free(s->str);
  577. //
  578. // s->str = tmp;
  579. // s->len = s->len + add->len;
  580.  
  581. *_s = s;
  582. }//
  583.  
  584. //加入传统 C 字符串
  585. void LString_AppendCString(lstring * s, char * str, int len)
  586. {
  587. lstring add = StringConst(str, len, s->pool);
  588.  
  589. printf("add len:%d", add.len);
  590. LString_Append(s, &add);
  591.  
  592. }//
  593.  
  594. void LString_AppendConst(lstring * s, char * add)
  595. {
  596.  
  597. //printf("add len:%d", add->len);
  598. LString_AppendCString(s, add, strlen(add));
  599.  
  600. }//
  601.  
  602. //int LString_AppendConst(lstring * _s, const char * _add)
  603. //{
  604. // printf("ok4 \r\n");
  605. // lstring add = StringConst((char *)_add, strlen(_add));
  606. //
  607. // printf("ok1");
  608. // LString_Append(_s, &add);
  609. // printf("ok");
  610. //}//
  611.  
  612. //检查字符串指针是否合法,只是简单的方法,不可靠,但有一定作用
  613. int CheckPString(lstring * s)
  614. {
  615. if (s == NULL)
  616. {
  617. printf("CheckPString: error, string is NULL!!!\r\n"); //就目前的要自动释放的需求来说,是不能为 NULL 的,因为那样 pool 就没有传入了
  618. return ;
  619. }
  620. printf_err1("CheckPString: s->_init != NULL\r\n");
  621. if (s->_init != NULL) //其实对于现代的编译器和操作系统来说,如果 s 没有初始化,在这里就很可能崩溃,所以实际上是检测不出来的,所以首尾都打印一下看看这个过程没结束就是这里出错了,对性能要求高的地方,去掉 printf 宏就可以不打印了
  622. {
  623. printf("CheckPString: error, string is not init!!!\r\n"); //就目前的要自动释放的需求来说,是不能为 NULL 的,因为那样 pool 就没有传入了
  624. return ;
  625. }
  626.  
  627. printf_err1("CheckPString: s->_init != NULL ok.\r\n");
  628. return ;
  629. }//
  630.  
  631. //绑定各个成员函数
  632. void BindStringFunctions(lstring * s)
  633. {
  634. printf_err1("BindStringFunctions \r\n");
  635. s->Append = LString_Append;
  636. s->AppendConst = LString_AppendConst;
  637.  
  638. }//
  639.  
  640. #endif
  1. //操作 lstring * 的各种底层函数
  2. //因为 lstring 包含了传统 C 的 0 结尾,所以大部分可以直接代用 C 的函数,当然最好参照 golang 重写
  3.  
  4. #ifndef _L_STRING_FUNCTIONS_C_
  5. #define _L_STRING_FUNCTIONS_C_
  6.  
  7. #include <stdio.h>
  8. #include <string.h>
  9.  
  10. #include "lstring.c"
  11.  
  12. //delphi 转换方便函数//但 C 语言索引是从 0 开始,不是 d7 的从 1 开始,一定要注意
  13. //查找子串位置
  14. //php 的 strpos 基本上也是这样
  15. int pos(lstring * substr, lstring * s)
  16. {
  17. char * p = strstr(s->str, substr->str);
  18.  
  19. if (p == NULL) return -;
  20.  
  21. return p - (s->str);
  22. }
  23.  
  24. //substr(字符串,截取开始位置,截取长度)//从 0 开始
  25. //lstring * substr(lstring * s, int start, int len)
  26. lstring * substring(lstring * s, int start, int len)
  27. {
  28. lstring r;
  29. lstring * sub;
  30. char * p = NULL;
  31.  
  32. //if (start + len > s->len) return NULL;//如果太多
  33. if (start + len > s->len) len = (s->len) - start;//如果太多返回后面的
  34.  
  35. p = s->str + start;
  36.  
  37. r = StringConst(p, len, s->pool);
  38. //r = StringCopy(r); //StringConst 不分配内存的,所以要复制一个新的出来
  39.  
  40. //sub = PString(r);
  41. //autofree_pstring(s->pool, sub);//返回字符串跟着 s 一起释放
  42.  
  43. sub = PStringCopy(&r);
  44.  
  45. return sub;
  46.  
  47. }
  48.  
  49. //字符串是否相等 //类似 java 的 equals
  50. int str_equals(lstring * s1, lstring * s2)
  51. {
  52. if (s1 == NULL && s2 == NULL) return ;
  53.  
  54. if (s1 == NULL || s2 == NULL) return ; //两者不可能都为空的情况下有一个为空,那就是不相等了
  55.  
  56. if (s1->len != s2->len) return ; //长度不等肯定也不是
  57.  
  58. if ( == strncmp(s1->str, s2->str, s1->len) ) return ;
  59.  
  60. return ;
  61. }
  62.  
  63. int streq(lstring * s1, lstring * s2)
  64. {
  65. return str_equals(s1, s2);
  66. }
  67.  
  68. int str_equals_c(lstring * s1, char * s2)
  69. {
  70.  
  71. if ( == strcmp(s1->str, s2) ) return ;
  72.  
  73. return ;
  74. }
  75.  
  76. //3) 前加#,将标记转换为字符串.
  77. //#define C(x) #x
  78. //则C(1+1) 即 ”1+1”.
  79.  
  80. //#define C(x) #x
  81. #define C(x)
  82. #define C1(a1, x, a2) strcmp(a1, a2)
  83.  
  84. //int ee_-()
  85. int t()
  86. {
  87. C(==);
  88. C1("",==,""); //可以这样模拟一个 == 号出来
  89.  
  90. }
  91.  
  92. //常用简写而已
  93. int seq(lstring * s1, char * s2)
  94. {
  95.  
  96. return str_equals_c(s1, s2);
  97. }
  98.  
  99. //替换单个字符,在需要高效时使用,因为替换一长串比较慢
  100. //str_replace
  101. lstring * str_replace_ch(lstring * s, char ch, char newch)
  102. {
  103. lstring * r = PStringCopy(s);
  104.  
  105. int i;
  106.  
  107. for (i=; i<r->len; i++)
  108. {
  109. if (r->str[i] == ch)
  110. r->str[i] = newch;
  111. }
  112.  
  113. return r;
  114.  
  115. }//
  116.  
  117. //主要用于判断空字符串//delphi 风格
  118. int length(lstring * s)
  119. {
  120. if (s == NULL) return ;
  121.  
  122. return s->len;
  123. }
  124.  
  125. //delphi 风格
  126. int Length(lstring * s)
  127. {
  128. return length(s);
  129. }
  130.  
  131. //主要用于判断空字符串//golang 风格
  132. int len(lstring * s)
  133. {
  134. if (s == NULL) return ;
  135.  
  136. return s->len;
  137. }
  138.  
  139. //转换为小写,注意这个在中文下会有问题//为了兼容字符串自动释放,当参数为空时只好返回空,要不找不到父节点
  140. lstring * lowercase(lstring * s)
  141. {
  142. lstring * r = NULL;
  143. int i;
  144. if (s == NULL)
  145. {
  146. printf("lowercase: error, string is NULL!!!"); //就目前的要自动释放的需求来说,是不能为 NULL 的,因为那样 pool 就没有传入了
  147. return NULL;
  148. }
  149.  
  150. //检查字符串指针是否合法,只是简单的方法,不可靠,但有一定作用
  151. CheckPString(s);
  152.  
  153. r = PStringCopy(s);
  154. //r->pool = s->pool; //让原字符串释放它
  155. //autofree_pstring(s->pool, r); //让原字符串释放它
  156.  
  157. for (i = ; i < r->len; i++)
  158. {
  159. r->str[i] = tolower(r->str[i]);
  160. }
  161.  
  162. //没有返回值,确实有问题
  163.  
  164. return r;
  165. }
  166.  
  167. //转换为大写,注意这个在中文下会有问题//为了兼容字符串自动释放,当参数为空时只好返回空,要不找不到父节点
  168. lstring * uppercase(lstring * s)
  169. {
  170. lstring * r = NULL;
  171. int i;
  172. if (s == NULL)
  173. {
  174. printf("lowercase: error, string is NULL!!!"); //就目前的要自动释放的需求来说,是不能为 NULL 的,因为那样 pool 就没有传入了
  175. return NULL;
  176. }
  177.  
  178. //检查字符串指针是否合法,只是简单的方法,不可靠,但有一定作用
  179. CheckPString(s);
  180.  
  181. r = PStringCopy(s);
  182. //r->pool = s->pool; //让原字符串释放它
  183. //autofree_pstring(s->pool, r); //让原字符串释放它
  184.  
  185. for (i = ; i < r->len; i++)
  186. {
  187. r->str[i] = toupper(r->str[i]);
  188. }
  189.  
  190. //没有返回值,确实有问题
  191.  
  192. return r;
  193. }
  194.  
  195. //与传统 get_value 不同,这个的匹配结尾字符串为查找互的第一个而不是最后一个
  196. //应该不区分大小写
  197. //要注意 C 语言的字符串是从 0 起始,而原来 delphi 的是从 1 起始的,所以 d7 转换过来的算法不能全部照搬的
  198. lstring * get_value_first(lstring *s, lstring * b_sp, lstring * e_sp)
  199. {
  200.  
  201. //开始复制的位置
  202. int b_pos = ;
  203. //复制结束的位置
  204. int e_pos = ;
  205. lstring * ls;
  206. lstring * r = NULL;
  207.  
  208. if (len(e_sp) == ) e_pos = length(s);
  209.  
  210. ls = lowercase(s);
  211. b_sp = lowercase(b_sp);
  212. e_sp = lowercase(e_sp);
  213.  
  214. //检查字符串指针是否合法,只是简单的方法,不可靠,但有一定作用
  215. CheckPString(ls);
  216. CheckPString(b_sp);
  217. CheckPString(e_sp);
  218. //--------------------------------------------------
  219.  
  220. b_pos = pos(b_sp, ls);
  221.  
  222. //if (length(b_sp) == 0) b_pos = 1;
  223. if (length(b_sp) == ) b_pos = ;
  224.  
  225. //if (b_pos == 0)
  226. if (b_pos == -) //没找到
  227. {
  228. r = NewString("", s->pool);
  229. //autofree_pstring(s->pool, r); //返回的字符串跟着 s 一起释放
  230. return r;
  231. };
  232.  
  233. b_pos = b_pos + length(b_sp);
  234. r = substring(s, b_pos, length(s));
  235. //result = copy(s, b_pos, length(s));
  236.  
  237. //--------------------------------------------------
  238.  
  239. //e_pos = pos(e_sp, lowercase(r)) - 1;
  240. e_pos = pos(e_sp, lowercase(r));
  241.  
  242. if (e_pos == -) e_pos = length(r);
  243.  
  244. //r = substring(r, 1, e_pos);
  245. r = substring(r, , e_pos);
  246.  
  247. return r;
  248. }
  249.  
  250. //c 语言的参数
  251. lstring * getValueFirst_c(lstring *s, char * b_sp, char * e_sp)
  252. {
  253. return get_value_first(s, NewString(b_sp, s->pool), NewString(e_sp, s->pool));
  254.  
  255. }
  256.  
  257. //c 语言的参数
  258. lstring * get_value_first_c(lstring *s, char * b_sp, char * e_sp)
  259. {
  260. return get_value_first(s, NewString(b_sp, s->pool), NewString(e_sp, s->pool));
  261.  
  262. }
  263.  
  264. #endif

一步一步从原理跟我学邮件收取及发送 5.C语言的socket示例的更多相关文章

  1. 一步一步从原理跟我学邮件收取及发送 2.邮箱的登录和绕不开的base64

    一步一步从原理跟我学邮件收取及发送 2.邮箱的登录和绕不开的base64 好了,经过本系列上一篇文章 "1.网络命令的发送",假设大家已经掌握了 email 电子邮件的命令发送的方 ...

  2. 一步一步从原理跟我学邮件收取及发送 4.不同平台下的socket

    既然是面向程序员的文章那当然不能只说说原理,一定要有实际动手的操作.    其实作为我个人的经历来说,对于网络编程,这是最重要的一章! 作为一位混迹业内近20年的快退休的程序员,我学习过很多的开发语言 ...

  3. 一步一步从原理跟我学邮件收取及发送 11.完整的发送示例与go语言

    经过了这个系列的前几篇文章的学习,现在要写出一个完整的 smtp 邮件发送过程简直易如反掌.    例如我们可以轻松地写出以下的纯 C 语言代码(引用的其他C语言文件请看文末的 github 地址): ...

  4. 一步一步从原理跟我学邮件收取及发送 3.telnet命令行发一封信

    首先要感谢博客园管理员的及时回复,本系列的第二篇文章得以恢复到首页,这是对作者的莫大鼓励.说实在的本来我真的挺受打击的.好在管理员说只是排版上有些问题,要用代码块修饰下相关的信息.说来惭愧因为常年编码 ...

  5. 一步一步从原理跟我学邮件收取及发送 8.EHLO 命令详解

    我们在上一篇中解决了接收一行命令的问题后,就可以来具体的分析邮件发送过程中涉及到的 SMTP 协议内容了. 首先来看通讯过程中的第一个内容:服务器在客户端连接上来后会主动发送一个问好的信息,所以这第一 ...

  6. 一步一步从原理跟我学邮件收取及发送 10.四句代码说清base64

    经过前几篇的文章,大家应该都能预感到一定要讲解 base64 函数的内容了.是的,马上要到程序登录的代码,base64 是必须要实现的. base64 很早以前我就接触了,在项目中也很喜欢用.但每换一 ...

  7. 一步一步从原理跟我学邮件收取及发送 9.多行结果与socket的阻塞

    前几篇的文章发表后,有网友留言说没有涉及到阻塞的问题吗?在 socket 的编程当中,这确实是个很重要的问题.结合目前我们文章的内容进度,我们来看看为什么说阻塞概念很重要. 接着上篇的内容,当我们发送 ...

  8. 一步一步从原理跟我学邮件收取及发送 12.telnet命令行收一封信pop3

    本系列上一篇文章中我们就说到了,这一次我们要说 pop3 收信了.虽然我觉得应该先说完 mime 格式,不过估计大家已经不耐烦了 -- 怎么老在说发送啊?我们要看收取!    好吧,来啦,来啦!收取邮 ...

  9. 一步一步从原理跟我学邮件收取及发送 13.mime格式与常见字符编码

    在前面的本系列文章中我们已经学会了邮件的发送和收取.但在收取中我们看到的是一串串的乱码,回忆前面的发送过程,我们会奇怪:我们前面的邮件是明文啊.为什么明文的邮件明明也可以正常工作,还要弄乱码似的字符串 ...

随机推荐

  1. 【转】搭建spark环境 单机版

    本文将介绍Apache Spark 1.6.0在单机的部署,与在集群中部署的步骤基本一致,只是少了一些master和slave文件的配置.直接安装scala与Spark就可以在单机使用,但如果用到hd ...

  2. java多线程(八)-死锁问题和java多线程总结

    为了防止对共享受限资源的争夺,我们可以通过synchronized等方式来加锁,这个时候该线程就处于阻塞状态,设想这样一种情况,线程A等着线程B完成后才能执行,而线程B又等着线程C,而线程C又等着线程 ...

  3. .Net IOC框架入门之二 CastleWindsor

    一.简介 Castle是.net平台上的一个开源项目,为企业级开发和WEB应用程序开发提供完整的服务,用于提供IOC的解决方案.IOC被称为控制反转或者依赖注入(Dependency Injectio ...

  4. Struts2下载

    package com.tcf.action; import java.io.BufferedInputStream; import java.io.FileInputStream; import j ...

  5. mybatis动态sql排序无效

    order by 字段,在用动态sql时会出现问题,排序无效,而且在日志里查询不到,不能发现这个错误. 通常,咱们的动态sql一般都会用#代替$,因为#可以防止sql注入问题. 但是在order by ...

  6. SpringMVC的映射器、适配器、解析器

    1.处理器和适配器 1.1springmvc的映射器 根据客户端请求的url,找到处理本次请求的handler(处理器),将url和controller关联起来 1.2springmvc的适配器 对映 ...

  7. JavaScript基础知识(JSON、Function对象、原型、引用类型)

    19.JSON 概念:JavaScript 对象表示法(JavaScript Object Notation),是一种轻量级的数据交换格式  特点:易于程序员编写和查看:易于计算机解析和生成 数据结构 ...

  8. webAPI+angularJS文件上传和下载

    开发框架 前端 angularJS1.6 下载和保存文件FileSaver:https://github.com/eligrey/FileSaver.js/ 后端 .net WebAPI 1 导入Ex ...

  9. vue.js之路由

    Vue.js本身只提供数据与视图绑定及组件化等功能,如果想用它来开发一个完整的SPA(单页面应用),我们就还需要使用一些Vue.js的插件.今天我学习一种叫做Vue-router的插件,用来提供路由管 ...

  10. Javascrip随笔1

    isNaN:指示某个值不是数字 文本字符串中使用反斜杠对代码行进行换行; 在计算机程序中,经常会声明无值的变量.未使用值来声明的变量,其值实际上是 undefined.在执行过以下语句后,变量 car ...