# CPPS 文檔 v1.0.0 [](https://gitee.com/cppscript/cpps/stargazers)[](https://github.com/johnsonyl/cpps)
CPPS是一種語法類似C++的輕量級嵌入式腳本語言,它擁有目前主流語言所擁有的很多特性包括協程,面向對象,Lambda,閉包,泛型變量,自定義模塊支持,GC垃圾回收以及跨平臺.CPPS通過內置語法解析將程序解釋成字節碼在虛擬機中運行.
CPPS只需要幾行代碼就可以很容易的嵌入到C++代碼中,并且用戶可以自己利用c++的類與函數擴展給CPPS以便CPPS處理更廣泛的領域,例如人工智能,GUI,網站以及游戲開發.
CPPS遵循MIT許可證協議,免費使用,但CPPS并不能保證程序穩定沒有任何BUG.希望您再使用過程中發現任何問題可以提交給作者.如果您有解決方案可以幫助CPPS更茁壯的成長.
作者QQ: 88481106
EMAIL: 88481106@qq.com
CPPS-QQ交流群: 282081601
[TOC]
# 1.如何編譯CPPS.
在本文中我們會詳細的介紹如何在三大主流平臺編譯運行CPPS.
如果在過程中有任何問題,都可以聯系作者EMAIL:88481106@qq.com 或者添加QQ群:282081601
## **1.1 在windows上編譯**
絕大多數開發者應該都是在windows上開發.然后在其他平臺編譯運行,因為在windows中擁有最強的VS編輯器.
但是同時windows開發的缺點就是很多三方庫的編譯最讓人苦惱.
CPPS自身并不需要任何三方庫所以編譯CPPS幾乎并不費力氣,作者使用的是Visual Studio 2019版本
如果你也是VS2019那么編譯會變得非常簡單.
\
我們第一步只需要打開VS2019 Native tools ,本文中為x64模式編譯.我們非常推薦使用x64模式.

找不到的小伙伴可以自行百度如何打開.
進入到工作目錄輸入
```
cmake .
```
就可以生成VS所需要的.sln項目工程. 只要打開 libcpps.sln 就可以編譯cpps了.
但麻煩事隨之而來,我們需要編譯內置擴展庫.**它在libs文件夾.**,這非常重要.
我們緊接著在VS2019 Native tools輸入
```
cd libs
cmake .
```
就可以生成cppslibs.sln
它使用了很多著名的三方庫 zlib,libcurl,openssl,libevent,hiredis,mysqlclient.所以在編譯擴展模塊之前.我們需要編譯這些三方庫.
這里教你如何編譯 zlib openssl libcurl [跳轉連接](編譯-windows-x64-openssl-zlib-libcurl.md)
hiredis 我在目錄中已經放置好了.在 **CPPS項目目錄/deps/** 中 ,只需要編譯一下即可
mysqlclient 我也放置在里面了. 在 **CPPS項目目錄/deps/deps/include/mysql** 以及 **CPPS項目目錄/deps/deps/lib** 是官方已經編譯好的lib 與 dll.
只需要把dll拷貝到bin目錄就可以使用了!
libevent 在編譯完zlib與openssl后 它會非常簡單.小伙伴們百度一下就可以找到很多文章.\
\
在windows上放置目錄我推薦分別放到**CPPS項目目錄/deps/deps/include** 與**CPPS項目目錄/deps/deps/lib**目錄,這樣非常方便統一管理以及在多平臺編譯. 如果大家都遵循這樣的方式,那么就很容易把自己的模塊分享給其他人.
至此相信小伙伴們已經編譯好了上述所需要的三方庫.同時也正確的放到了相應的目錄中
我們只要進入libs/cppslibs.sln然后很容易的就可以編譯過了.
\
恭喜你,你已經成功的編譯好cpps了.
> **注:但實際上我會把我編譯好的頭文件,lib與dll分別放到deps里.不過它是vs2019 x64編譯的,如果你和我的環境一樣,你會省去很多時間.如果你和我不一樣的環境,請先刪除它們.然后按照上續步驟重新編譯安裝三方庫**
## **1.2 在linux上編譯**
linux 編譯起來就不像windows那么麻煩,因為很多系統都提供了devel開發版本.不過最頭疼的就是linux有上百個版本的操作系統.大家安裝軟件的方式各不相同.作者目前就僅僅在centos ubuntu 編譯了,并且寫了2個快捷編譯的批處理為**build-centos.sh**與**build-ubuntu.sh**
分別還有 build-debian.sh 與 build-fedora.sh 作者并沒有深入嘗試,希望有此系統的小伙伴可以嘗試使用它編譯并且把最終正確的方法提供給作者以更新讓更多小伙伴可以順利簡化編譯.
\
希望小伙伴們可以為作者提供更多版本操作系統的快捷編譯批處理.這樣其他小伙伴在使用編譯時就會簡單多了.
\
如果并非上面所說的操作系統.那么您可以按照下面步驟去編譯它.
我們需要進入到項目目錄cmake它,生成makefile文件.
```
cmake .
make
make install
```
我們就可以順利的編譯完成cpps,因為cpps并不需要任何第三方庫.
下面我們需要編譯內置擴展庫模塊.
它使用了很多著名的三方庫 zlib,libcurl,openssl,libevent,hiredis,mysqlclient.所以在編譯擴展模塊之前.我們需要編譯這些三方庫.
hiredis我放到了deps目錄下.除此之外小伙伴們需要到各自的官網下載,并且按照它們提供的方法編譯安裝它們.
至此編譯安裝好第三方庫后就可以進入libs目錄編譯內置擴展模塊了.
```
cd libs
cmake .
make
```
\
恭喜你,你已經成功的編譯好cpps了!
## **1.3 在macos上編譯**
在蘋果系統上編譯比較簡單,只要保證xcode是最新的.然后使用終端進入cpps目錄
```
cmake .
make
make install
```
下面我們需要編譯地址擴展模塊.
下面我們需要編譯內置擴展庫模塊.
它使用了很多著名的三方庫 zlib,libcurl,openssl,libevent,hiredis,mysqlclient.所以在編譯擴展模塊之前.我們需要編譯這些三方庫.
```
brew install zlib
brew install openssl
brew install curl
brew install libevent
brew install mysql-connector-c
brew install hiredis
```
我們推薦小伙伴們安裝brew, 百度有如何安裝homebrew的方法.建議使用中科大或者阿里云的源.
brew 可以幫你更快捷的安裝cpps內置擴展模塊所需的三方庫.
至此編譯安裝好第三方庫后就可以進入libs目錄編譯內置擴展模塊了.
```
cd libs
cmake .
make
```
\
恭喜你,你已經成功的編譯好cpps了!
# 2.數據類型介紹
CPPS支持8大數據類型,下面分別向您介紹他們的作用.
## 2.1 整數
整數的類型是cpps_integer(long long),它比如(-20,0,5,29,100)無小數部分.
```
var i = 100;
println(i);
結果:
100
```
你也可以通過 +,-,*,/ 來操作它, 也可以使用()用來分組.
```
var i = 100 * 5 - (20 * 3) * (100-98);
println(i);
結果:
380
```
同樣我們還支持16進制的整數定義.它會在運行時變成10進制的數.
```
var i = 0x10;
println(i);
結果
16
```
## 2.2 字符串
CPPS 提供了std::string 的擴展,你可以跟string一樣的方式來使用字符串.并且在它之上增加了很多內置函數更易操作.
在定義字符串的時候我們可以使用雙引號("....") 或者單引號('....') 來定義字符串.反斜杠提供轉譯操作方法與c++是一致的.
```
var s = "John:\"hello world\"";
println(s);
結果
John:"hello world"
```
類似std::string內置函數.
```
var s = "abcdefg";
var pos = s.find("d");
if(pos != string.npos)
println(s.substr(pos));
結果
defg
```
字符串拼接 我們使用了類似lua的方式,使用了..來拼接字符串.并沒有使用 +
```
var s1 = "hello ";
var s2 = "world";
println(s1..s2);
結果
hello world
s1 ..= "world";
println(s1);
結果
hello world
```
利用fotmat方式拼接字符串,這是非常好用也是最常用的一個字符串操作功能,它非常高效.遠遠高于..的拼接.非常推薦使用這種方式.
```
var age = 32;
var name = "johnson"
var about = "My Name is {name}, I'm {age} years old.";
println(about);
結果
My Name is johnson, I'm 32 years old.
//甚至在{}中還可以使用各種表達式,例如調用函數
var test()
{
return "Working";
}
about = "My Name is {name}, I'm {age} years old. I'm {test()} Now";
println(about);
結果:
My Name is johnson, I'm 32 years old. I'm Working Now
```
### ***class String***
在CPPS中封裝了很多std::string內部函數,并且在此基礎上增加了一些實用的函數.但是還有一些函數作者并沒全部封裝,如果您有遇到沒有封裝的可以與作者取得聯系.
#### ***String::size()***
返回String中的長度
*****
#### ***String::find(var text,var pos = 0)***
在字符串中查找內容。在字符串中搜索由其參數指定的序列的第一次出現。當指定pos時,搜索僅包括位置pos處或之后的字符,忽略包括pos之前的字符在內的任何可能的事件。
*****
#### ***String::rfind(var text,var pos = string.end)***
倒序查找內容,在字符串中搜索由其參數指定的序列的第一次出現。當指定pos時,搜索僅包括位置pos處或之前的字符,忽略包括pos之后的字符在內的任何可能的事件。
*****
#### ***String::replace(var src,var tar)***
本函數與stl中的replace有所不同,cpps中替換為全文替換.
*****
#### ***String::clear()***
清空String的所有內容.
*****
#### ***String::copyto(String tarstring)***
目標必須是一個字符串變量,將自己的內容拷貝進去.這樣可以保證不會在new出一個新的String.
*****
#### ***String::split(var sep,var count = -1)***
根據sep拆分成數組,當指定了count時只拆分count個數的數組. 當count為-1時則全文拆分.
*****
#### ***String::cut(var len)***
拆分成多個len長度的字符串數組.
*****
#### ***String::strcut(var left,var right)***
根據left與right來截取字符串.
```
var s = "您當前的驗證碼為:[102345],感謝您使用XXX.";
var code = s.strcut("[","]");
println(code);
結果:
102345
```
*****
#### ***String::strcuts(var left,var right)***
根據left與right截取相匹配的多個字符串.
```
var a = "<a src='http://www.baidu.com/'>下載一</a><a src='http://www.google.com/'>下載二</a>";
var srcs = a.strcuts("src='","'");
println(srcs);
結果
[http://www.baidu.com/,http://www.google.com/,]
```
*****
#### ***String::empty()***
判斷一個String是否為空.如果為空則返回true.
*****
#### ***String::substr(var pos,var len = string.npos)***
截取一段字符串.當len存在則根據pos位置截取len長度的字符串.
*****
#### ***String::at(var idx)***
返回String中idx的字節(整數)
*****
#### ***String::tolower()***
本函數并不修改自身,而是返回一個轉為小寫的新字符串.如果想修改自身可以使用string.lower(str);
*****
#### ***String::toupper()***
本函數并不修改自身,而是返回一個轉為大寫的新字符串.如果想修改自身可以使用string.upper(str);
*****
#### ***String::trim()***
將左右兩側的空格剔除.
*****
#### ***String::ltrim()***
將左側的空格剔除.
*****
#### ***String::rtrim()***
將右側的空格剔除.
*****
#### ***String::join(vector vct)***
將vct列表中每一個對象轉換成字符串并追加一個String自身.
```
var s = ",";
var vct = [1,2,3,4,5,6];
println(s.join(vct));
結果:
1,2,3,4,5,6,
```
*****
#### ***String::startswith(var str)***
如果String中開頭為str則返回為true.否則false
*****
#### ***String::endswith(var str)***
如果String中結尾為str則返回為true.否則false
*****
#### ***String::pop_back(var count = -1)***
當count 被指定數量時則移除尾部count數量的字節.
*****
#### ***String::push_back(var charcode)***
向String末尾插入一個charcode.
*****
#### ***String::append(var str)***
向String末尾追加一個str字符串.
*****
## 2.3 布爾
布爾類型與c++的用法一致.
```
var b = true;
println(b)
b = false;
println(b)
結果
true
false
```
## 2.4 浮點數
在cpps里我們使用的是double類型,并非float. 它與整數一樣同樣支持 +,-,*,/ 的方式來計算.與浮點數計算后最終結果都會轉成浮點數.
```
var i= 5; //整數
var f = i + 1.22; //轉換成浮點
println(f);
結果
6.22
```
## 2.5 函數
函數的定義也與c++相類似.我們的類型只有泛型所以返回類型只有var一種.
在定義參數時,我們可以給出默認參數,當調用是不傳參默認為設置的值. 如果沒有設置默認值.那則為nil (null).
```
var funcname(var p1,var p2 = 100)
{
println(p1);
println(p2);
}
funcname("text");
funcname(1.5,"text");
結果:
text
100
1.5
text
```
## 2.6 class 類
CPPS的類為標準面向對象,它可以是繼承多個父類,這個操作與c++一致,只有一點需要注意我們的函數不支持多態,因為所有變量均為泛型.多態并沒辦法支持.不過用戶可以根據傳進來的類型實現類似多態.
下面演示一些基礎的類操作
```
class A
{
????//同名的為構造函數
A()
????{
println("A:A();");
????}
var test()
????{
println("A::test();");
????}
}
class B?:?A
{
????var?name?=?"johnson";?//可以在這里直接設置值
????var?age;
????var?state;
B()
????{
????????age?=?32;?//也可以在構造函數里設置值
println("B:B()");
????}
var test()
????{
println("B::test()");
A::test();
????}
}
var?b?=?newB(){
????state?=?"Working"?//也可以在這里設置值
};
b.test();
println("My?Name?is?{b.name},?I'm?{b.age}?years?old.?I'm?{b.state}?Now");
結果:
A:A();
B:B()
B::test()
A::test();
My Name is johnson, I'm 32 years old. I'm Working Now
```
## 2.7 namespace 名空間
CPPS里面保留了名空間的關鍵字與用法. 用法接近于c++,但是并沒有c++那么健壯.暫時未實現using namespace功能.
```
namespace cpps
{
class A
{
A()
{
println("cpps::A::A()");
}
}
var func()
{
println("cpps::func()");
}
var abc = 100;
}
var a = new cpps::A();
cpps::func();
println(cpps::abc);
結果
cpps::A::A()
cpps::func()
100
```
## 2.8 module 模塊
CPPS允許在腳本中定義模塊標簽,或者擴展C++提供的模塊.它看上去和名空間很像,但是又略有不同.
```
//并非重寫sys,而是在系統的sys基礎上擴展新變量,函數或者是類.
module sys
{
var test()
{
println("sys.test()");
}
class A
{
var name = "johnson";
}
}
sys.test();
var a = new sys::A();
println(a.name);
結果
sys.test()
johnson
```
## 2.9 Lambda函數
CPPS中Lambda函數支持類似JS的閉包 并且定義lambda函數與C++ 幾乎一致.
下面來演示一種閉包的操作.
```
var outer() {
var result = [];
result.resize(10);
for (var i = 0; i<10;i++){
result[i] = [](var num){
return [](){
println(num); // 此時訪問的num,是上層函數執行環境的num,數組有10個函數對象,每個對象的執行環境下的number都不一樣
};
}(i);
}
return result;
}
var l = outer();
foreach(var n : l)
{
n();
}
結果:
0
1
2
3
4
5
6
7
8
9
```
## 2.10 vector
CPPS中vector就是對std::vector的一層封裝.幾乎用法也與vector一致.但是可能有一些函數并沒全部實現,如果有需要作者添加的可以聯系作者添加.或者在github上 pullrequest. 我會非常感謝.
下面看一下使用的例子.
```
//方法1.
var a = new vector();
a.push_back(100);
println(a);
//方法2
var b = new vector[10];// 創建一個10長度的數組
b[0] = 100;
b[9] = 200;
println(b);
//方法3 (推薦)
var c = [1,2,3,4,5,6,7];
println(c);
結果:
[100,]
[100,nil,nil,nil,nil,nil,nil,nil,nil,200,]
[1,2,3,4,5,6,7,]
```
您也可以查看更多vector內置函數.
## 2.11 map
CPPS中map使用了[https://github.com/greg7mdp/parallel-hashmap](https://github.com/greg7mdp/parallel-hashmap)來替代unordered_map與map,這個hashmap的速度要比stl的快10-60倍.非常強烈推薦.里面的函數也與stl的map 幾乎一致.
下面看一下使用例子.
```
//方法1.
var a = new map();
a.insert("key","value");
a["key2"] = "value2";
println(a["key"]);
println(a);
//方法2
var b = {
key : "value",
key2 : "value2"
};
println(b);
結果:
value
{key:value,key2:value2,}
{key:value,key2:value2,}
```
您也可以查看更多map內置函數.
# 3.關鍵字語句
## 3.1 if
CPPS中if 的使用方法與C++一致.
```
print("Please enter an integer:");
var x = int(io.getc());
if(x < 0)
{
x = 0;
println("Negative changed to zero");
}
else if(x == 0)
println("Zero");
else if(x == 1)
println("Single");
else
println("More");
結果
Please enter an integer:42
More
```
## 3.2 for
CPPS中的for 與c++操作基本一致.C++11之后的for功能使用了foreach來操作.
下面看一下例子:
```
for(var i = 0; i < 10; i++ )
{
println(i);
}
結果:
0
1
2
3
4
5
6
7
8
9
```
## 3.3 foreach
在C++11中 for可以遍歷有迭代器的變量了.那么現在cpps也有類似的功能了 只是叫foreach.
下面來看一下如何使用的.
```
//range
println("range")
foreach(var i : range(10))
{
println(i);
}
//vector
println("vector");
var a = [1,2,3,4,5,6,7];
foreach(var i : a)
{
println(i);
}
//map
println("map");
var b = {
a:1,
b:3,
c:5
};
foreach(var it : b)
{
println("key={it.first()},value={it.second()}");
}
結果
range
0
1
2
3
4
5
6
7
8
9
10
vector
1
2
3
4
5
6
7
map
key=a,value=1
key=b,value=3
key=c,value=5
```
## 3.4 while
while與C++操作一致.
下面看下用法示例
```
var a = 0;
while(a < 10)
{
println(a++);
}
結果:
0
1
2
3
4
5
6
7
8
9
```
## 3.5 break
break 與 C++操作一致
```
var i = 0;
while(true)
{
if(i > 10) break;
println(i);
i++;
}
結果
0
1
2
3
4
5
6
7
8
9
10
```
## 3.6 continue
continue 與C++操作一致
```
foreach(var i : range(10))
{
if(i % 2 == 0) continue;
println(i);
}
結果
1
3
5
7
9
```
## 3.7 try catch thorw
捕獲異常機制與c++部分一致.暫時還不是很強壯.例如final 還未實現下個版本在實現吧.
下面看下例子
```
try
{
var a = null;
a.asd();
}
catch(var e)
{
println("{e.geterrstr()}?line:{e.getline()}?file:{e.getfile()}");
println(e.callstack());
}
結果:
[a] must be a class object or a domain before the '.' line:4 file:main.cpp
Error stack information:
```
當然也可以自定義異常.
```
class diyerror
{
diyerror(var id,var msg)
{
errorid = id;
err = msg;
}
var errorid ;
var err;
}
try
{
throw new diyerror(10,"test msg");
}
catch(var e)
{
var err = e.getvalue();
println( type(err) ); //可以通過type來判斷是什么錯誤.
println(err.errorid);
println(err.err);
}
結果
diyerror
10
test msg
```
### ***class cpps_trycatch_error***
#### ***cpps_trycatch_error::geterrno()***
*****
#### ***cpps_trycatch_error::geterrstr()***
*****
#### ***cpps_trycatch_error::getfile()***
*****
#### ***cpps_trycatch_error::getline()***
*****
#### ***cpps_trycatch_error::getvalue()***
*****
#### ***cpps_trycatch_error::callstack()***
*****
## 3.8 return
return 增加了類似c++17的多返回值,但是使用更加便捷簡單.
下面看下示例.
```
//單返回值
var func2()
{
return "sadf";
}
//多返回值
var func1()
{
return 1,2,func2();
}
//接收多返回值
var?[a,b,e]?=?func1();
println(a);
println(b);
println(e);
結果:
1
2
sadf
```
## 3.9 const
在CPPS里面不像C++一樣有編譯錯誤.所以你要強制修改一個const的變量,并沒有過多的錯誤提示.但是它們并不能真正的給const變量賦值.這里需要開發者多多注意. 切記不要嘗試給const變量修改值,導致修改不成功影響程序運行.
下面看一下代碼示例:
```
const var idx = 100;
println(idx);
idx = 1000; // here is not work..
println(idx);
結果
100
100
```
## 3.10 async await yield
CPPS中支持了協程,但是值得一提的是CPPS中asio相關的擴展庫還不多.不過作者也會盡快開發相關異步模塊提供給協程使用.
async 來定義一個函數是否為協程函數.當使用了async時它就不能被直接執行了,而是需要創建協程任務在協程中調用. 例如await 或者 create_task.
await 用來等待一個協程任務執行結束并獲取返回值.
yield 用來通知協程可切換其他協程運行. 當前協程無任務可做了.但是并不退出.
下面看一下基礎的asyncio如何使用吧.
```
async var test()
{
println("do test function");
await asyncio.sleep(2000);
println("do test function done");
return "test";
}
async var test1()
{
println("do test1 function");
await asyncio.sleep(1000);
println("do test1 function done");
var a ;
a.asd();
return "test1";
}
var test_call_back(var task,var context)
{
println(task.get_name());
println(context);
}
async var main(){
var task1 = asyncio.create_task(test());
task1.set_name("Custom names are used to distinguish");
task1.add_done_callback(test_call_back,"Custom context content");
var task2 = asyncio.create_task(test1());
yield; //暫時讓其他協程先運行.
var ret = await asyncio.wait(task1);
if(ret.timeout())
{
println("task1 was timeouted.");
}
println("state:{ret.state()}");
println(ret.result());
try{
println(await task2);
}
catch(var e)
{
println("oh,we catch some error");
println("{e.geterrstr()} line:{e.getline()} file:{e.getfile()}");
println(e.callstack());
}
println("finish");
}
asyncio.run(main());
結果:
do test function
do test1 function
do test1 function done
do test function done
Custom names are used to distinguish
Custom context content
state:2
test
oh,we catch some error
[a] must be a class object or a domain before the '.' line:16 file:main.cpp
Error stack information:
main.cpp [30] test1
Error stack information:
main.cpp [54] main
finish
```
> 下面還有更詳細的asyncio介紹.請查看asyncio模塊.
## 3.11 new
new 操作與C++一致. 唯獨的區別就是不需要delete, cpps自身有GC (垃圾回收) 可以根據使用狀態釋放應該釋放的對象.
```
class A{
var a = 100;
}
var a = new A();
println(a.a);
結果
100
```
## 3.12 #include
方法與C++一致.在解釋時把文件載入到緩沖區一并解釋成字節碼.這里需要注意的是,它并不是在運行時才載入.而是在loadfile時一并按順序載入.
下面看一下代碼示例
```
a.cpp
var abc = 100;
b.cpp
#include <a.cpp>
println(abc);
結果
100
```
## 3.13 #import
這里是cpps 模塊導入方法.解釋格式為 #import 字符串或者數組. 數組可以讓用戶批量載入很多模塊.
下面看下代碼示例
```
#import "uuid"
#import "base64"
或者
#import ["uuid","base64"]
var id = uuid.uuid4();
println(id);
println(base64.encode(id));
結果
51319FBD-AA3E-4298-87F6-8DE10F3A4891
NTEzMTlGQkQtQUEzRS00Mjk4LTg3RjYtOERFMTBGM0E0ODkx
```
## 3.14 dofile
這里dofile與#include 是有區別的.它是在運行時才會載入進來.并且無論你在任何地方調用,它的作用域都在全局,而不在當前作用域.
下面看下代碼示例
```
a.cpp
var abc = 100;
b.cpp
var test()
{
dofile("a.cpp");
}
test();
println(abc);
結果
100
```
## 3.15 dostring
dostring 也是在運行時才會載入,它的作用域會在當前運行的作用域中,如果需要把一整個文件載入進來請不要使用dofile 而是載入文件到一個字符串,然后dostring.
下面看一下代碼示例
```
dostring("println('hello world');");
結果
hello world
```
## 3.16 注釋
注釋與C++一致,支持// 與 /**/
```
//這里是一行注釋
/*
這里是多行注釋
可以隨意定義注釋內容與行數.
*/
```
# 4.內置模塊
## 4.1 基礎API(base)
### ***4.1.1 print(var v)***
向控制臺打印一個對象.可以是數字也可以是字符串.或者是vector,map.
```
print(1);
print("100");
```
*****
### ***4.1.2 println(var v)***
它與print功能一致,只是會在輸出的末尾增加一個換行符.
*****
### ***4.1.2 print_color(var v,var color)***
它與print功能一致.并且可以設置顏色.
*****
### ***4.1.2 println_color(var v,var color)***
它與print功能一致,只是會在輸出的末尾增加一個換行符.并且可以設置顏色.
*****
### ***4.1.3 dump(var v)***
dump是為了讓用戶更好的調試或者格式化自己的類對象.用戶只要在類中實現dump函數,就可以讓任何人dump自己的類對象
下面看下代碼示例
```
class A
{
var s = "10000";
var dump()
{
return "dump s = {s}";
}
}
var a = new A();
var s = dump(a);
println(s);
結果
dump s = 10000
```
*****
### ***4.1.4 exit(var exitcode)***
它就是調用stdlib的exit函數. 讓程序在此時此刻退出進程
```
exit(0);
```
*****
### ***4.1.5 sleep && Sleep(var ms)***
開頭字母可以是大寫,也可以是小寫.同等于調用linux的 usleep MSVC的Sleep,讓程序休眠 ms 毫秒
```
sleep(1000) //休眠1秒
```
*****
### ***4.1.6 tonumber && double(var a)***
將一個變量強制類型轉換為浮點型
```
var a = "16.2";
var d = tonumber(a);
println(d);
var d2 = double(a);
println(d2);
結果
16.2
16.2
```
*****
### ***4.1.7 toint && int(var v)***
將一個變量強制類型轉換為整數型
```
var d = 16.2;
var i = toint(d);
println(i);
var i2 = int(d);
println(i2);
結果
16
16
```
*****
### ***4.1.8 tostring && str(var v)***
強制類型轉換為字符串
```
var a = 100;
var s = tostring(a);
println(s);
var s2 = str(a);
println(s2);
結果
100
100
```
*****
### ***4.1.9 isstring(var v)***
判斷一個變量是否為字符串類型
*****
### ***4.1.10 isint(var v)***
判斷一個變量否為整數型
*****
### ***4.1.11 isbool(var v)***
判斷一個變量是否為布爾類型
*****
### ***4.1.12 isvector(var v)***
判斷一個變量是否為數組
*****
### ***4.1.13 ismap(var v)***
判斷一個變量是否為map
*****
### ***4.1.14 isnull(var v)***
判斷一個變量是否為NULL
*****
### ***4.1.15 isnumber(var v)***
判斷一個變量是否為浮點型
*****
### ***4.1.16 isfunction(var v)***
判斷一個變量是否為函數
*****
### ***4.1.17 isclassvar(var v)***
判斷一個變量是否為對象
*****
### ***4.1.18 isclass(var v)***
判斷一個變量是否為類
*****
### ***4.1.19 objtype(var v)***
返回變量的類型 可以參見 ot
```
var a = 10;
if(objtype(a) == ot.int)
println("is int");
```
*****
### ***4.1.20 type(var v)***
返回字符串格式的變量類型
```
var a = 10;
println(type(a));
結果
integer
```
*****
### ***4.1.21 system(var cmd)***
調用stdlib的system,cmd命令詳情參考各操作系統說明.
*****
### ***4.1.22 len(var v)***
支持類型vector map 以及 字符串. 返回他們的size函數
```
var vec = [1,2,3,4];
println(len(vec));
var s = "abcdefg";
println(len(s));
結果
4
```
*****
### ***4.1.23 sort(var vec)***
使用快速排序法為vector進行排序.
```
var vec = [2,1,5,3,32,7,10,4];
sort(vec);
println(vec)
結果
[1,2,3,4,5,7,10,32,]
```
*****
### ***4.1.23 shuffle(var vec)***
使用快速洗牌法為vector進行打亂順序.
```
var vec = [1,2,3,4,5,6,7];
shuffle(vec);
println(vec)
結果
[7,1,2,4,3,6,5,]
```
### ***4.1.23 reverse(var vec)***
將vector數組反序.
```
var vec = [1,2,3,4,5,6,7];
reverse(vec);
println(vec)
結果
[7,6,5,4,3,2,1,]
//另一種操作方法
var vec2 = vec.reverse(); // 拷貝了一份反序的數組. 不改變原vector.
println(vec2)
結果
[7,6,5,4,3,2,1,]
```
*****
### ***4.1.24 isset(var v)***
判斷一個變量是否存在或者不為null
```
if(isset(a))
println(a); // a不存在所以什么都不會輸出.
```
*****
### ***4.1.25 assert(var bool)***
斷言.不過多介紹了. C++開發者最熟悉不過的了.
*****
### ***4.1.26 getargs()***
獲取main(int argc,char **argv) 中的參數,返回格式為vector.
```
cpps test.cpp 123 456 789
test.cpp
println(getargs());
結果
[cpps.exe,test.cpp,123,456,789,]
```
*****
### ***4.1.27 execmd(var cmd)***
類似system,但是它可以把執行結果返回給程序.
*****
### ***4.1.28 now()***
獲取當前unix時間戳,同等于 time.getime()
````
var t = now();
````
*****
## 4.2 sys
### ***4.2.1 ***sys.platform******
sys.platform 包含操作系統的一些信息 譬如windows分win32與win64,linux分linux32與linux64,macos分macos32與macos64等.
*****
### 4.2.2 ***sys.esayplanform***
它就比較簡單, 只有windows,linux,macos(目前只支持這3中操作系統)
*****
### 4.2.3 ***sys.builder_version***
它包含了編譯器的版本信息.
*****
### 4.2.4 ***sys.version***
它包含了CPPS最詳細的編譯版本信息.
*****
### 4.2.5 ***sys.versionno***
它為數字版本的CPPS版本號.1.0.0 對應是 10000 ,例如51.21.23 版本 = 512123版本. cpps 沒有百位版本號.
*****
### 4.2.6 ***sys.debug***
判斷是否為debug編譯版本.
*****
## 4.3 ot
可以把ot看做成枚舉,但是cpps暫時還沒支持枚舉.
```
enum ot
{
int,
bool,
string,
classvar
function
domain,
ptr,
number,
nil,
lambda
}
```
*****
## 4.4 environ
系統支持環境變量
*****
### ***environ.get(var key)***
獲取系統環境變量
```
var path = environ.get("PATH");
```
*****
### ***environ.set(var key,var value)***
設置系統環境變量
```
environ.set("SELFKEY","SELFVALUE");
```
*****
## 4.5 serializer
序列化是一個非常有用的功能.它可以把一個對象變成一個map或者vector,然后在從一個vector或者map 反序列化成一個對象.
*****
### ***serializer.encode(var v,var type = serializer.vector)***
序列化會默認序列化成數組格式.
```
class A
{
var a = 10;
var b = 20;
var c = 30;
}
var a = new A();
println(serializer.encode(a,serializer.vector));
println(serializer.encode(a,serializer.map));
結果:
[10,20,30,]
{a:10,b:20,c:30,}
```
*****
### ***serializer.decode(var __class,var v)***
```
class A
{
var a ;
var b ;
var c ;
}
var arr = [20,50,100];
var a = serializer.decode(A,arr);
println(a.a);
println(a.b);
println(a.c);
結果:
20
50
100
```
*****
### ***serializer.vector***
代表序列化成vector格式.
*****
### ***serializer.map***
代表序列化成map格式.
*****
## 4.6 console
### ***console.color(var __color)***
其實每個平臺中控制臺的字體顏色很多,但是顏色相近的很少,我整理出7種相近的不過已經夠用了.
下面可以嘗試一下.
```
foreach(var i : range(6))
{
console.color(i);
println("color:{i}");
console.clearcolor();
}
結果(windows)
0 黑色
1 紅色
2 綠色
3 黃色
4.藍色
5 紫色
6 青色
```

*****
### ***console.clearcolor()***
將控制臺字體還原成初始顏色(一般為白色).
```
var i = 1; //red
console.color(i);
println("color:{i}");
console.clearcolor();
```
*****
### ***console.clear()***
清屏功能,在windows下system("cls"),linux或者mac上為system("clear");
在這集合成一個函數,更方便使用了.
*****
## 4.7 debug
### ***debug.open()***
啟用debug模式
*****
### ***debug.close()***
關閉debug模式
*****
### ***debug.log(var v)***
根據debug模式是否打印日志.
*****
### ***debug.breakpoint()***
斷點模式.可以在斷點時利用cpps語法調試代碼.
```
var a = 10;
debug.breakpoint();
println(a);
結果:
quit debug command : quit
debug>a = 20
debug>quit
20
```
*****
### ***debug.trace()***
這是一個非常好用的調試功能,它可以把當前作用域中所有存在的變量都打印出來,
下面看一下代碼示例
```
class A
{
var a = 20;
var b = 2000;
var c = 5000;
}
var func()
{
var a = 200;
var b = new A();
var c = 1000;
debug.trace();
}
func();
結果:
a = 200
b = class <A>
{
b.a = 20
b.b = 2000
b.c = 5000
}
c = 1000
```
*****
## 4.8 time
### ***time.gettime()***
獲取當前UNIX時間戳返回為整數型(單位秒)
*****
### ***time.gettimestr()***
獲取當前UNIX時間戳返回為字符串型(單位秒)
```
var ts = time.gettimestr();
println(ts);
結果:
2020-12-29 13:04:19
```
*****
### ***time.maketime(var year,var mon,var day,var hour,var min,var sec)***
生成一個UNIX系統時間.返回為整數型.
```
var t = time.maketime(2020,12,29,13,00,00);
println(t);
結果
1609218000
```
*****
### ***time.gettickcount()***
獲取從開機到現在所用的毫秒數.
```
var tc = time.gettickcount();
println(tc);
結果:
18024223
```
*****
### ***time.time2str(var t)***
將整數型UNIX時間轉換成字符串類型時間戳.
```
var t = time.gettime();
var ts = time.time2str(t);
println(ts);
結果:
2020-12-29 13:34:43
```
*****
### ***time.str2time(var ts)***
將字符串類型UNIX時間戳轉換到整數型時間戳
```
var ts = "2020-12-29 13:34:43";
var t = time.str2time(ts);
println(t);
結果:
1609220083
```
*****
### ***time.issomeday(var t1,var t2)***
判斷2個整數型時間戳是否為同一天.
*****
### ***time.getyear(var t)***
根據整數型時間戳獲取年份
*****
### ***time.getmon(var t)***
根據整數型時間戳獲取月份
*****
### ***time.getday(var t)***
根據整數型時間戳獲取本月天數
*****
### ***time.gethour(var t)***
根據整數型時間戳獲取本日小時部分
*****
### ***time.getmin(var t)***
根據整數型時間戳獲取本小時分鐘部分
*****
### ***time.getsec(var t)***
根據整數型時間戳獲取本分鐘描述部分.
*****
### ***time.getyday(var t)***
根據整數型時間戳獲取是本年的某天.
*****
### ***time.getwday(var t)***
根據整數型時間戳獲取是本周的某天
*****
### ***time.addmonths(var t,var mons)***
在整數型時間戳上增加或減少mons月.
*****
### ***time.adddays(var t,var days)***
在整數型時間戳上增加或減少days天.
*****
### ***time.addyears(var t,var years)***
在整數型時間戳上增加或減少years年.
*****
### ***time.addweeks(var t,var weeks)***
在整數型時間戳上增加或減少weeks周
*****
### ***time.addhours(var t,var hours)***
在整數型時間戳上增加或減少 hours 小時
*****
### ***time.addminutes(var t,var mins)***
在整數型時間戳上增加或減少 mins 分鐘
*****
### ***time.addseconds(var t,var secs)***
在整數型時間戳上增加或減少 secs 秒鐘
*****
### ***time.strftime(String fmt,var unixtime)***
將unix時間戳格式化成字符串
```
var t = time.maketime(2017,1,1,12,0,5);
var t2 = time.strftime("%F",t);
println(t2);
結果
2017-01-01
```
格式化參數
```
%a 星期幾的簡寫
%A 星期幾的全稱
%b 月份的簡寫
%B 月份的全稱
%c 標準的日期的時間串
%C 年份的前兩位數字
%d 十進制表示的每月的第幾天
%D 月/天/年
%e 在兩字符域中,十進制表示的每月的第幾天
%F 年-月-日
%g 年份的后兩位數字,使用基于周的年
%G 年份,使用基于周的年
%h 簡寫的月份名
%H [24小時制]的小時
%I 12小時制的小時
%j 十進制表示的每年的第幾天
%m 十進制表示的月份
%M 十時制表示的分鐘數
%n 新行符
%p 本地的AM或PM的等價顯示
%r 12小時的時間
%R 顯示小時和分鐘:hh:mm
%S 十進制的秒數
%t 水平[制表符]
%T 顯示時分秒:hh:mm:ss
%u 每周的第幾天,星期一為第一天 (值從1到7,星期一為1)
%U 第年的第幾周,把星期日作為第一天(值從0到53)
%V 每年的第幾周,使用基于周的年
%w 十進制表示的星期幾(值從0到6,星期天為0)
%W 每年的第幾周,把星期一做為第一天(值從0到53)
%x 標準的日期串
%X 標準的時間串
%y 不帶世紀的十進制年份(值從0到99)
%Y 帶世紀部分的十制年份
%z,%Z 時區名稱,如果不能得到時區名稱則返回空字符。
%% [百分號]
```
*****
## 4.9 io
### ***io.getc()***
獲得從控制臺輸入的文字 實際調用的是std::cin .所以遇到空格,回車,換行就會觸發截停.
```
var c = io.getc();
println(c);
結果
hello world
hello
```
* 因為hello world中間有空格 就只讀取到了hello 剩下的world需要在調用一次io.getc()才可以獲取到.如果想要獲取一整行可以調用io.getline()
*****
### ***io.getline()***
獲得從控制臺輸入的一行文字.C++ 實際調用是getline(std::sin,ret);
*****
### ***io.fopen(var path,var mode)***
打開一個文件并返回句柄
mode 模式與c++一致.
```
“r” :以只讀方式打開文件,該文件必須存在。
“w” :打開只寫文件,若文件存在則文件長度清為0,即該文件內容會消失。若文件不存在則建立該文件。
“a” :以附加的方式打開只寫文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾,即文件原先的內容會被保留。(EOF符保留)
“r+” :以可讀寫方式打開文件,該文件必須存在。
“w+” :打開可讀寫文件,若文件存在則文件長度清為零,即該文件內容會消失。若文件不存在則建立該文件。
“a+”:以附加方式打開可讀寫的文件。若文件不存在,則會建立該文件,如果文件存在,寫入的數據會被加到文件尾后,即文件原先的內容會被保留。 (原來的EOF符不保留)
“rb” :只讀打開一個二進制文件,只允許讀數據。
“wb” :只寫打開或建立一個二進制文件,只允許寫數據。
“ab” :追加打開一個二進制文件,并在文件末尾寫數據。
“rb+” :讀寫打開一個二進制文件,允許讀寫數據,文件必須存在。
“wb+” :讀寫打開或建立一個二進制文件,允許讀和寫。
“ab+” :讀寫打開一個二進制文件,允許讀,或在文件末追加數據。
“rt” :只讀打開一個文本文件,只允許讀數據。
“wt” :只寫打開或建立一個文本文件,只允許寫數據。
“at” :追加打開一個文本文件,并在文件末尾寫數據。
“rt+” :讀寫打開一個文本文件,允許讀和寫。
“wt+” :讀寫打開或建立一個文本文件,允許讀寫。
“at+” :讀寫打開一個文本文件,允許讀,或在文件末追加數據。
```
*****
### ***io.writefile(var path,var content)***
快速寫入一個文件.content為字符串類型.
```
var content = "abcdefg";
io.writefile("a.txt",content);
```
*****
### ***io.readfile(var path)***
快速讀取一個文件返回字符串類型里,字符串實際類型為std::string,所以并不會因為\0而少讀取資源.可根據size函數獲取長度.
```
a.txt
abcdefg
var fcontent = io.readfile("a.txt");
println(fcontent);
結果
abcdefg
```
*****
### ***io.filesize()***
快速返回一個文件的大小
```
var fsize = io.filesize("a.txt");
```
*****
### ***io.fsize(FILE file)***
使用fopen打開的文件句柄來獲取文件大小.
```
var file = io.fopen("a.txt","rb");
var filesize = io.fsize(file);
```
*****
### ***io.fread(FILE file,Buffer buf,var len)***
使用fopen打開的文件句柄來讀取數據.第二個參數為Buffer.
下面看一下代碼示例
```
var buf = new Buffer();
var file = io.fopen("a.txt","rb");
if(file)
{
var filesize = io.fsize(file);
io.fread(file,buf,filesize);
io.fclose(file);
}
println(buf.tostring());
結果
abcdefg
```
*****
### ***io.fwrite(FILE file,Buffer buf)***
使用fopen打開的文件句柄來寫入數據.第二個參數為Buffer
下面看一下代碼示例
```
var buf = new Buffer();
buf.writeString("abcdefg");
var file = io.fopen("a.txt","wb+");
if(file)
{
io.fwrite(file,buf);
io.fclose();
}
```
*****
### ***io.fwrites(FILE file,var str)***
使用fopen打開的文件句柄來寫入數據.第二個參數為字符串
```
var file = io.fopen("a.txt","wb+");
if(file)
{
io.fwrites(file,"abcdefg");
io.fclose();
}
```
*****
### ***io.fseek(FILE file,var offset,var origin)***
使用fopen打開的文件句柄來操作游標,與C++使用方法一致.
```
io.fseek(file,0,io.SEEK_END);
```
*****
### ***io.fclose(FILE file)***
關閉一個文件句柄.
*****
### ***io.fflush(FILE file)***
使用fopen打開的文件句柄來沖洗流中的信息
*****
### ***io.remove(var filepath)***
刪除路徑中的文件.
*****
### ***io.copy(var srcfilepath,var tarfilepath)***
拷貝一個文件.
*****
### ***io.move(var srcfilepath,var tarfilepath)***
剪切一個文件
*****
### ***io.copydir(var srcdirpath,var tardirpath)***
拷貝一個文件夾.
*****
### ***io.movedir(var srcdirpath,var tardirpath)***
剪切一個文件夾
*****
### ***io.rename(var srcname,var newsrcname)***
重命名一個文件或文件夾
*****
### ***io.getfileext(var filepath)***
得到一個路徑中文件的擴展名
*****
### ***io.getfilepath(var filepath)***
得到一個路徑中文件的路徑部分
*****
### ***io.getfilename(var filepath)***
得到一個路徑中文件名字部分
*****
### ***io.getfilenamenotext(var filepath)***
得到一個路徑中文件名字部分但去掉擴展名
*****
### ***io.getcwd()***
獲取當前程序的工作目錄.
*****
### ***io.mkdir(var dirpath)***
創建一個文件夾.它不能創建多層文件夾.如果需要創建多層需要使用 ***io.mkdirs***
*****
### ***io.rmdir(var dirpath)***
刪除一個文件夾,它不能刪除還存在內容的文件夾,如果需要強行刪除帶有內容的文件夾請使用 ***io.rmdirs***
*****
### ***io.mkdirs(var dirpath)***
創建多層文件夾.
*****
### ***io.chdir(var dirpath)***
修改工作目錄.
*****
### ***io.isdir(var path)***
判斷目錄是否為文件夾
*****
### ***io.isfile()***
判斷目錄是否為文件
*****
### ***io.normpath(path)***
路徑格式化.這個功能與python中的normpath一致.將所有'/' '\\' 一律統一為 '\\' , 將../或者./ 修改成最終正式目錄.
```
var path = "d:\\work\\code\\bin/script/../cpps.exe";
path = io.normpath(path);
println(path);
path.pop_back();
path.replace("\\","/");
println(path);
結果:
d:\work\code\bin\cpps.exe\
d:/work/code/bin/cpps.exe
```
* 值得注意的是它會在最后增加一個\\ 我們需要根據需要刪除它. path.pop_back();, 當linux時需要整體把'\\'替換成'/'. path.replace("\\","/");
*****
### ***io.isabspath(var path)***
判斷一個路徑是否為絕對路徑.
```
var path1 = "d:/work/";
println(io.isabspath(path1));
var path2 = "work/code";
println(io.isabspath(path2));
var path3 = "/work/code";
println(io.isabspath(path3));
結果
true
false
true
```
*****
### ***io.splitdrive(var path)***
把一個路徑拆分數組,0位為盤符,1位為絕對路徑
```
var path = "d:/work/code";
var l = io.splitdrive(path);
println(l[0]);
println(l[1]);
結果
d:
/work/code
```
*****
### ***io.getrealpath()***
獲取當前可執行文件的真實路徑,非工作路徑.
*****
### ***io.file_exists(var path)***
檢測文件是否存在.
*****
### ***io.walk(var path,var findchildren = true)***
遍歷文件夾中所有文件與文件夾
```
var filelist = io.walk("d:/work/code");
println(filelist);
```
*****
### ***io.listdir(var path,var findchildren = true)***
只遍歷文件夾中的文件夾.
```
var filelist = io.listdir("d:/work/code");
println(filelist);
```
*****
### ***io.stat(var path)***
獲取文件或文件夾的stat屬性并返回一個class statinfo對象.
*****
### ***class statinfo***
#### ***statinfo::dev()***
返回stat.st_dev
*****
#### ***statinfo::ino()***
返回stat.st_ino
*****
#### ***statinfo::mode()***
返回stat.st_mode
*****
#### ***statinfo::nlink()***
返回stat.st_nlink
*****
#### ***statinfo::uid()***
返回stat.st_uid
*****
#### ***statinfo::gid()***
返回stat.st_gid
*****
#### ***statinfo::rdev()***
返回stat.st_rdev
*****
#### ***statinfo::size()***
返回stat.st_size
*****
#### ***statinfo::atime()***
返回stat.st_atime
*****
#### ***statinfo::mtime()***
返回stat.st_mtime
*****
#### ***statinfo::ctime()***
返回stat.st_ctime
*****
#### ***statinfo::isdir()***
判斷是否為文件夾
*****
#### ***statinfo::isreg()***
判斷是否為文件
*****
#### ***statinfo::isblk()***
是否是一個塊設備
*****
#### ***statinfo::ischr()***
是否是一個字符設備
*****
#### ***statinfo::isfifo()***
是否是一個FIFO文件
*****
#### ***statinfo::islink()***
是否是一個連接
*****
#### ***statinfo::issock()***
是否是一個SOCKET文件
*****
### ***io.last_write_time(var filepath)***
獲取文件最后修改時間.
*****
### ***io.sep***
在windows系統中路徑分隔符為'\\',在linux與unix系統中路徑分割符為'/'
*****
### ***io.linesep***
在windows系統中換行符為"\r\n",在linux與unix系統中換行符為'\n'
*****
## 4.10 math
### ***math.abs(var num)***
取num的絕對值并返回.
*****
### ***math.srand(var seed)***
設置隨機數種子. 常用的是方法是io.srand(time.gettime());
*****
### ***math.randf()***
返回0-1之間的隨機數.
*****
### ***math.rand()***
返回一個整數的隨機.非C++的 rand(). 最大值非RAND_MAX.
*****
### ***math.random(var min,var max)***
返回min-max之間的隨機數.
*****
### ***math.acos(var x)***
返回以弧度表示的 x 的反余弦
*****
### ***math.asin(var x)***
返回以弧度表示的 x 的反正弦
*****
### ***math.atan(var x)***
返回以弧度表示的 x 的反正切
*****
### ***math.ceil(var x)***
返回大于或等于 x 的最小的整數值
*****
### ***math.cos(var x)***
返回x的弧度的余弦值
*****
### ***math.exp(var x)***
返回 e 的 x 次冪的值
*****
### ***math.floor(var x)***
返回小于或等于 x 的最大的整數值
*****
### ***math.fmod(var x,var y)***
返回 x 除以 y 的余數
*****
### ***math.log(var x)***
返回 x 的自然對數
*****
### ***math.max(var x,var y)***
返回 x 與 y 中最大的數.
*****
### ***math.min(var x,var y)***
返回 x 與 y 中最小的數.
*****
### ***math.sin(var x)***
返回的x弧度的正弦值
*****
### ***math.sqrt(var x)***
返回數字x的平方根
*****
### ***math.tan(var x)***
返回x弧度的正切值
*****
### ***math.HUGE***
官方解釋:
當函數的結果不可以表示為浮點數時。如果是因為結果的幅度太大以致于無法表示,則函數會設置 errno 為 ERANGE 來表示范圍錯誤,并返回一個由宏 HUGE_VAL 或者它的否定(- HUGE_VAL)命名的一個特定的很大的值。
如果結果的幅度太小,則會返回零值。在這種情況下,error 可能會被設置為 ERANGE,也有可能不會被設置為 ERANGE。
*****
### ***math.maxinteger***
返回 ((cpps_integer)(MAXUINT64 >> 1)) 即為9223372036854775807
*****
### ***math.mininteger***
返回 ((cpps_integer)~MAXINT64) 即為 -9223372036854775808
*****
### ***math.pi***
返回 3.14159265358979323846
*****
## 4.11 string
**在老版本cpps中未封裝std::string,所以都是使用string模塊輔助字符串的一些操作.所以很多函數都可以在字符串類中操作了.**
### ***string.find(String _str,var text,var pos = 0)***
同String::find();
*****
### ***string.rfind(String _str,var text,var pos = 0)***
同String::rfind();
*****
### ***string.length(String _str)***
同String::size();
*****
### ***string.strlen(String _str)***
內部實現為 strlen(_str.c_str()); 真是獲取文本的長度.遇到'\0'則停止.
*****
### ***string.replace(String _str,var src,var tar)***
同String.repalce();
*****
### ***string.clear(String _str)***
同String::clear();
*****
### ***string.copyto(String srcsstr,String tarstr)***
同String::copyto();
*****
### ***string.split(String _str,var sep,var count = -1)***
同String::split();
*****
### ***string.cut(String _str,var len)***
同String::cut();
*****
### ***string.strcut(String _str,var left,var right)***
同String::strcut();
*****
### ***string.strcuts(String _str,var left,var right)***
同String::strcuts();
*****
### ***string.empty(String _str)***
同String::empty();
*****
### ***string.substr(String _str,var pos,var len = string.npos)***
同String::substr();
*****
### ***string.npos***
同c++中std::string::npos.
*****
### ***string.at(String _str,var idx)***
同String::at();
*****
### ***string.format(var fmt,...)***
類似sprintf操作. 比較古老的一種方式了.
```
c 字符
d 或 i 有符號十進制整數
e 使用 e 字符的科學科學記數法(尾數和指數)
E 使用 E 字符的科學科學記數法(尾數和指數)
f 十進制浮點數
g 自動選擇 %e 或 %f 中合適的表示法
G 自動選擇 %E 或 %f 中合適的表示法
o 有符號八進制
s 字符的字符串
u 無符號十進制整數
x 無符號十六進制整數
X 無符號十六進制整數(大寫字母)
p 指針地址
n 無輸出
% 字符
```
*****
### ***string.lower(String _str)***
將_str中英文全部轉為小寫.
*****
### ***string.upper(String _str)***
將_str中英文全部轉為大寫.
*****
### ***string.ltrim(String _str)***
同String::ltrim();
*****
### ***string.rtrim(String _str)***
同String::rtrim();
*****
### ***string.trim(String _str)***
同String::trim();
*****
### ***string.join(String _str,vector vct)***
同String::join();
*****
### ***string.between(var left,var right,vector vct)***
類似String::join, 將vct中的每一個字符串對象左側增加left,右側增加right.
```
var vct = [1,2,3,4,5,6,7];
println(string.between("<",">,",vct));
結果
<1>,<2>,<3>,<4>,<5>,<6>,<7>,
```
*****
### ***string.startswith(String _str,var str)***
同String::startswith();
*****
### ***string.endswith(String _str,var str)***
同String::endswith();
*****
### ***string.chr(var charcode)***
將一個charcode轉成字符串.(不推薦使用)
*****
### ***string.push_back(String _str,var charcode)***
同String::push_back();
*****
### ***string.unicode_charCodeAt(String _str,var idx)***
獲取string中unicode編碼值.
*****
### ***string.unicode_fromCodeAt(String _str,vector vct)***
把vct中的unicode編碼添加到_str中.
*****
## 4.12 GC
### ***GC.Collect()***
清理新生代
*****
### ***GC.CollectAll()***
清理新生代&老生代
*****
## 4.13 asyncio
協程,又稱微線程,纖程。它運行在單線程中,可以實現多任務并行執行.由cpps自身控制上下文切換.可以減少更多函數等待時間,從而大大的增加運行速度.
### ***asyncio.get_event_loop()***
獲取asyncio協程主循環對象.
*****
### ***asyncio.create_task(asyncio::ASYNC_OBJECT func)***
創建一個協程任務,這里創建后并加入循環隊列執行.等待await獲取結果.
```
async var testasio()
{
return 100;
}
async var main()
{
var task1 = asyncio.create_task(testasio());
var ret = await task1;
println(ret);
}
asyncio.run(main());
結果
100
```
await 并不一定需要create_task.
await testasio(); 方法也可以執行.
在協程中有一些函數并不需要返回值.只要關注執行進度的時候,create_task就比較實用了.
*****
### ***asyncio.sleep(var ms)***
協程中的休眠,休眠ms毫秒,它不同于sleep.此函數為協程函數.
*****
### ***asyncio.wait(var asyncfunc)***
asyncfunc可以是ASYNC_OBJECT 或者 ASYNC_TASK, 它會等待直到函數運行結束返回ASYNC_TASK對象.
```
async var testasio()
{
return 100;
}
async var main()
{
var ret = await asyncio.wait(testasio());
println("state:{ret.state()}");
println(ret.result());
ret = NULL;
}
asyncio.run(main());
結果:
state:2
100
```
*****
### ***asyncio.wait_for(asyncio::ASYNC_OBJECT func,var ms)***
同wait函數類似,但是它有一個等待時間,超出等待時間會讓強制停止協程運行并返回ASYNC_TASK對象
> 但是await asyncio.sleep(2000000); 并不會因為wait_for停止而停止.在開發協程時需注意wait_for不要丟給業務層.要在可能會引起超時的外部函數時來決定調用wait_for最佳,否可能導致并沒有真正意義上結束掉協程.
但main()主協程結束時,會強制停止所有協程.
```
async var testasio()
{
await asyncio.sleep(2000000); //沒有真正被停掉.
return 100;
}
async var main()
{
var ret = await asyncio.wait_for(testasio(),1000); //只等待1秒.
if(ret.timeout())
{
println("task1 was timeouted.");
}
println("state:{ret.state()}");
println(ret.result());
ret = NULL;
}
asyncio.run(main());
結果:
task1 was timeouted.
state:4
nil
```
> 下面推薦的做法是.
```
async var testasio(var timeout)
{
await asyncio.wait_for(asyncio.sleep(2000000),timeout); //這樣才能保證正確停止.
return 100;
}
async var main()
{
var ret = await testasio(1000); //只等待1秒.
println(ret);
ret = NULL;
}
asyncio.run(main());
結果:
100
```
*****
### ***asyncio.run(var task)***
在協程中我們推薦使用asyncio.run來開始啟動協程,原則上推薦全程序只有一個run被執行.例如以下方式
```
async var main()
{
//do something..
}
asyncio.run(main());
```
*****
### ***asyncio.pending***
協程狀態,代表已創建但未加入隊列運行.
*****
### ***asyncio.running***
協程狀態,代表已經在運行中
*****
### ***asyncio.done***
協程狀態,代表已經運行完成
*****
### ***asyncio.cancelled***
協程狀態,代表已經被取消運行
*****
### ***asyncio.timeout***
協程狀態,代表已經運行超時.
*****
### ***class asyncio::ASYNC_OBJECT***
協程函數當被執行時并非真實運行,而是生成一個ASYNC_OBJECT對象.等待加入到任務隊列.
```
async var test()
{
return 100;
}
var async_object = test();
println(async_object);
結果
class <ASYNC_OBJECT>
```
*****
### ***class asyncio::ASYNC_LOOP***
協程的主循環對象.負責管理協程的上下文切換,已經運行周期的管理.
#### ***ASYNC_LOOP::run_until_complete(var task)***
并不推薦用戶直接調用這個函數.建議用戶調用asyncio.run函數來替代這個接口.
task可以是一個ASYNC_OBJECT或ASYNC_TASK,同樣可以是一個列表.
用戶可以并發執行這些任務直至所有任務結束.
```
var loop = asyncio.get_event_loop();
async var test1()
{
await asyncio.sleep(100); //等待100毫秒后再執行,當test3執行完的時候我還沒有休眠完成,在讓給test2執行
println("test1");
}
async var test2()
{
yield; //先讓其他協程執行.
println("test2"); //當test3執行完并且test1沒休眠完成時,test2就可以優先完成.
}
async var test3()
{
println("test3"); //沒人先執行完,那我先執行結束.
}
var tasks = [
test1(),
test2(),
test3()
];
loop.run_until_complete(tasks);
結果:
test3
test2
test1
```
*****
### ***class asyncio::ASYNC_TASK***
ASYNC_TASK 為協程任務實體,它包含著ASYNC_OBJECT運行對象,以及上下文數據,還有返回值,以及運行狀態等信息.在做協程相關操作時它起到了至關重要的作用.
#### ***ASYNC_TASK::result()***
返回ASYNC_OBJECT運行后的返回值.如果期間任務被任何情況打斷時它返回為null.
*****
#### ***ASYNC_TASK::state()***
獲取任務當前協程狀態.
*****
#### ***ASYNC_TASK::cancelled()***
判斷是否被取消,如果是取消狀態則返回true,否則false.
*****
#### ***ASYNC_TASK::done()***
判斷是否完成,如果是完成狀態則返回true,否則false.
*****
#### ***ASYNC_TASK::timeout()***
判斷是否超時,如果是超時狀態則返回true,否則false.
*****
#### ***ASYNC_TASK::pending()***
判斷是否未運行,如果是未運行狀態則返回true,否則false.
*****
#### ***ASYNC_TASK::running()***
判斷是否運行中,如果是運行中狀態則返回true,否則false.
*****
#### ***ASYNC_TASK::set_name(var name)***
設置協程名稱,可在callback時根據協程名稱做一些邏輯判斷.
*****
#### ***ASYNC_TASK::get_name()***
獲取協程名稱.
*****
#### ***ASYNC_TASK::add_done_callback(var func,var context = nil)***
增加完成回調函數,context可以在完成時傳回到回調函數中.
*****
#### ***ASYNC_TASK::remove_done_callback()***
移除完成回調函數.
```
async var testasio()
{
return 100;
}
var done_call_back(var task,var context)
{
println(task.get_name());
println(context);
}
async var main()
{
var task1 = asyncio.create_task(testasio());
task1.set_name("er gou zi");
task1.add_done_callback(done_call_back,"gua wa zi");
var ret = await task1;
println(ret);
}
asyncio.run(main());
結果
er gou zi
gua wa zi
100
```
*****
# 5.內置擴展模塊
## 5.1 compress
### ***zlib***
本擴展模塊對zlib壓縮庫進行了封裝.
#### ***zlib.compress(var data,var level = zlib.Z_DEFAULT_COMPRESSION)***
zip方式壓縮,data可以是一個字符串格式或者是Buffer格式. level 默認為Z_DEFAULT_COMPRESSION壓縮比.壓縮成功則返回一個字符串類型的對象.否則為nil
*****
#### ***zlib.decompress(var data,var uncompresssize = 4096)***
zip方式解壓縮.data可以是一個字符串格式或者是Buffer格式.uncompresssize 為未壓縮時真實大小.如果不知道大小可以盡可能給一個大一些的數,否則可能會導致數據不全或解壓縮失敗.如果解壓縮成功會返回一個字符串類型的對象,否則為nil
```
#import "compress"
var srcstr = "123456789";
//zlib
var tarstr = zlib.compress(srcstr);
println(string.length(tarstr));
var srcstr2 = zlib.decompress(tarstr);
println(srcstr2);
```
*****
#### ***zlib.adler32(var data,var adler = 1)***
adler32校驗算法.data可以是一個字符串格式或者是Buffer格式.
```
println(zlib.adler32("hello world"));
```
*****
#### ***zlib.crc32(var data,var crc = 1)***
crc32校驗算法.data可以是一個字符串格式或者是Buffer格式.
```
println(zlib.crc32("hello world"));
```
*****
#### ***zlib.Z_NO_COMPRESSION = 0***
*****
#### ***zlib.Z_BEST_SPEED = 1***
*****
#### ***zlib.Z_BEST_COMPRESSION = 9***
*****
#### ***zlib.Z_DEFAULT_COMPRESSION = (-1)***
*****
#### ***zlib.ZLIB_VERSION***
編譯時的zlib版本.
*****
#### ***zlib.ZLIB_RUNTIME_VERSION()***
當前動態庫的zlib版本.
*****
### ***gzip***
本gzip是對zlib中的gzip壓縮與解壓縮方式進行的封裝.
#### ***gzip.compress(var data,var level = gzip.Z_DEFAULT_COMPRESSION)***
gzip方式壓縮,data可以是一個字符串格式或者是Buffer格式. level 默認為Z_DEFAULT_COMPRESSION壓縮比.壓縮成功則返回一個字符串類型的對象.否則為nil
*****
#### ***gzip.decompress(var data,var uncompresssize = 4096)***
gzip方式解壓縮.data可以是一個字符串格式或者是Buffer格式.uncompresssize 為未壓縮時真實大小.如果不知道大小可以盡可能給一個大一些的數,否則可能會導致數據不全或解壓縮失敗.如果解壓縮成功會返回一個字符串類型的對象,否則為nil
```
#import "compress"
var srcstr = "123456789";
var tarstr = gzip.compress(srcstr);
println(string.length(tarstr));
var srcstr2 = gzip.decompress(tarstr);
println(srcstr2);
```
*****
#### ***gzip.Z_NO_COMPRESSION = 0***
*****
#### ***gzip.Z_BEST_SPEED = 1***
*****
#### ***gzip.Z_BEST_COMPRESSION = 9***
*****
#### ***gzip.Z_DEFAULT_COMPRESSION = (-1)***
*****
### ***tarfile***
tarfile 模塊可以用來讀寫 tar 歸檔,包括使用 gzip, (bz2 和 lzma 暫未實現) 壓縮的歸檔。 請使用 zipfile 模塊來讀寫 .zip 文件,或者使用 shutil 的高層級函數。
#### ***tarfile.open(var filepath,var mode = 'r',var bufsize = 10240)***
| 模式 | 動作 |
| --- | --- |
| `'r'or'r:*'` | 打開和讀取使用透明壓縮(推薦)。 |
| `'r:'` | 打開和讀取不使用壓縮。|
| `'r:gz'` | 打開和讀取使用gzip 壓縮。 |
| `'r:bz2'` | 打開和讀取使用bzip2 壓縮。 (暫未實現)|
| `'r:xz'` | 打開和讀取使用lzma 壓縮。(暫未實現) |
| `'x'`或`'x:'` | 創建tarfile不進行壓縮。如果文件已經存在,則返回空。 |
| `'x:gz'` | 使用gzip壓縮創建tarfile。如果文件已經存在,則返回空 |
| `'x:bz2'` | 使用bzip2 壓縮創建tarfile。如果文件已經存在,則返回空 (暫未實現)|
| `'x:xz'` | 使用lzma 壓縮創建tarfile。如果文件已經存在,則返回空 (暫未實現)|
| `'a'or'a:'` | 打開以便在沒有壓縮的情況下追加。如果文件不存在,則創建該文件。 |
| `'w'or'w:'` | 打開用于未壓縮的寫入。 |
| `'w:gz'` | 打開用于 gzip 壓縮的寫入。 |
| `'w:bz2'` | 打開用于 bzip2 壓縮的寫入。(暫未實現) |
| `'w:xz'` | 打開用于 lzma 壓縮的寫入。 (暫未實現)|
*****
#### ***class tarfile::tarfile***
##### ***tarfile::open(var filepath,var mode = 'r',var bufsize = 10240)***
也可以自己new一個對象,然后open文件.推薦使用tarfile.open
*****
##### ***tarfile::getmembers()***
獲取tarfile壓縮文件中所有成員.
*****
##### ***tarfile::extract(var member,var path,var set_attrs = true,var numeric_owner = false)***
提取一個tarfile歸檔中的成員到指定目錄.
*****
##### ***tarfile::extractall(var path,var membsers,var numeric_owner = false)***
將歸檔中的所有成員提取到指定目錄,如果給定了members則只提取members中的成員信息,但前提是members的成員是tarfile中的子集.
*****
##### ***tarfile::close()***
關閉 tarfile 在寫入模式下,會向歸檔添加兩個表示結束的零數據塊。
*****
##### ***tarfile::gettarinfo(var name,var arcname)***
name為指定文件路徑,根據stat信息創建一個tarfile_info對象, archname則為添加到tarfile中的路徑與名稱.一般從name中提取.如果未指定archname,則會統一歸檔至根目錄.
*****
##### ***tarfile::addfile(tarfile_info tarfileinfo)***
將gettarinfo獲取的tarfile_info對象添加到tarfile歸檔中,在添加歸檔前你可以對tarfile_info對象進行一下調整與修改.
*****
##### ***tarfile::getmember(var name)***
獲取指定name成員對象.
*****
#### ***class tarfile::tarfile_info***
##### ***tarfile_info::isblk()***
是否是一個塊設備
*****
##### ***tarfile_info::ischr()***
是否是一個字符設備
*****
##### ***tarfile_info::isdev()***
如果為字符設備、塊設備或 FIFO 之一則返回 true
*****
##### ***tarfile_info::isdir()***
是否是一個文件夾(目錄)
*****
##### ***tarfile_info::isfifo()***
是否是一個FIFO文件
*****
##### ***tarfile_info::islnk()***
是否是一個連接
*****
##### ***tarfile_info::issym()***
是否是一個符號鏈接
*****
##### ***tarfile_info::isfile()***
是否是一個文件
*****
```
#import "compress"
/tarfile
//tar解壓縮
var file = tarfile.open("test.tar","r",10240000);
file.extractall();
//tar.gz 解壓縮
var file = tarfile.open("test.tar.gz","r:gz",10240000);
file.extractall();
//tar.gz 寫入
var file = tarfile.open("test2.tar.gz","x:gz",10240000);
var fileinfo = file.gettarinfo("test.txt");
file.addfile(fileinfo);
file.close();
```
### ***zipfile***
ZIP 文件格式是一個常用的歸檔與壓縮標準。 這個模塊提供了創建、讀取、寫入、添加及列出 ZIP 文件的工具.
此模塊目前不能處理分卷 ZIP 文件。它可以處理使用 ZIP64 擴展(超過 4 GB 的 ZIP 文件)的 ZIP 文件。
#### ***zipfile.open(var filepath,var pwd = "",var mode = "r",var level = zlib.Z_DEFAULT_COMPRESSION)***
此函數用來打開或創建一個ZIP文件格式的歸檔與壓縮文件,如果打開成功則返回一個zipfile對象,如果打開失敗則返回nil.
*****
#### ***class zipfile::zipfile***
##### ***zipfile::open(var filepath,var pwd = "",var mode = "r",var level = zlib.Z_DEFAULT_COMPRESSION)***
此函數用來打開或創建一個ZIP文件格式的歸檔與壓縮文件,如果打開成功則返回true,否則返回false.
*****
##### ***zipfile::infolist()***
返回所有zipfile中成員對象列表,對象格式為zipfile_info.
*****
##### ***zipfile::namelist()***
返回所有zipfile中文件名稱(路徑)列表.
*****
##### ***zipfile::extract(var member,var path)***
將member一個成員提取到指定目錄.
*****
##### ***zipfile::extractall(var path,var members)***
將zipfile中的所有成員對象提到path目錄,如果給定members 則只將members中的成員提取到指定目錄.但要保證members列表中的成員是zipfile中的成員.
*****
##### ***zipfile::close()***
關閉歸檔文件。 你必須在退出程序之前調用 close() 否則將不會寫入關鍵記錄數據。
*****
##### ***zipfile::read(var name)***
將讀取指定文件到內存中,并返回. 返回類型為Buffer.
*****
##### ***zipfile::write(var name,var arcname)***
寫入一個文件到zipfile中,如果給定arcname則以arcname為名稱(路徑)存入zipfile,否則讀取name中的名稱部分存到zipfile的根目錄.
*****
##### ***zipfile::getinfo(var name)***
獲取一個zipfile_info對象.
*****
#### ***class zipfile::zipfile_info***
##### ***zipfile_info::comment()***
獲取文件注釋
*****
##### ***zipfile_info::compress_size()***
獲取壓縮后的大小
*****
##### ***zipfile_info::compress_type()***
獲取壓縮類型
*****
##### ***zipfile_info::CRC()***
獲取crc32校驗值
*****
##### ***zipfile_info::create_system()***
獲取創建文件時的系統
*****
##### ***zipfile_info::create_version()***
獲取創建文件時的版本
*****
##### ***zipfile_info::data_time()***
獲取文件添加zipfile時間.
*****
##### ***zipfile_info::extra()***
擴展字段數據。 PKZIP Application Note 包含一些保存于該 bytes 對象中的內部結構的注釋
*****
##### ***zipfile_info::extract_version()***
需要用來提取歸檔的 PKZIP 版本。
*****
##### ***zipfile_info::file_size()***
獲取文件原始大小
*****
##### ***zipfile_info::filename()***
獲取文件名稱
*****
##### ***zipfile_info::is_dir()***
是否為文件夾.
*****
```
#import "compress"
// //zipfile
var file = zipfile.open("test.zip");
file.extractall();
//輸出個別需要文件
foreach(var info: file.infolist()){
if(!info.is_dir()){
var buffer = file.read(info.filename());
if(buffer){
var filename = io.getfilename(info.filename());
println(filename);
var file = io.fopen(filename,"wb+");
if(file){
println("save ok");
io.fwrite(file,buffer);
io.fclose(file);
}
}
}
}
//zip 追加文件
var file = zipfile.open("test2.zip","","a");
file.write("client.cpp");
file.close();
```
## 5.2 base64
### ***base64.encode(String strobj)***
將一個String類型的對象進行base64加密.并返回一個String類型.
*****
### ***base64.decode(String strobj)***
將一個base64加密字符串解密成一個String類型對象.
*****
## 5.3 encoding
### ***encoding.encode(String str,var encodingtype = encoding.UTF8)***
將str轉成encodingtype類型字符串.
*****
### ***encoding.utf8_to_utf16(String utf8str)***
將一個utf8string 轉換成 utf16string.
*****
### ***encoding.utf16_to_utf8(String utf8str)***
將一個utf16string 轉換成 utf8string.
*****
### ***encoding.utf8_to_utf32(String utf8str)***
將一個utf8string 轉換成 utf32string.
*****
### ***encoding.utf32_to_utf8(String utf8str)***
將一個utf32string 轉換成 utf8string.
*****
### ***encoding.utf16_to_utf32(String utf8str)***
將一個utf16string 轉換成 utf32string.
*****
### ***encoding.utf32_to_utf16(String utf8str)***
將一個utf32string 轉換成 utf16string.
*****
### ***encoding.codepoint16_to_utf8(int16 codepoint16,String targetstr)***
將一個codepoint16寫入到targetstr.
> 注:target會被清空只保留codepoint,轉換失敗則targetstr為空字符串.
### ***encoding.codepoint_to_utf8(int32 codepoint,String targetstr)***
將一個codepoint32寫入到targetstr.
> 注:target會被清空只保留codepoint,轉換失敗則targetstr為空字符串.
*****
### ***encoding.UTF8 = 0***
*****
### ***encoding.GBK = 1***
*****
```
system("chcp 65001"); //use utf8 string
#import "encoding"
var s = "我是中文";
println(s); //我是中文
var utf8 = encoding.encode(s,encoding.UTF8);
println(utf8); //我是中文
var gbk = encoding.encode(s,encoding.GBK);
println(gbk);//無法顯示
utf8 = encoding.encode(gbk,encoding.UTF8);
println(utf8);//我是中文
var utf16 = encoding.utf8_to_utf16(s);
println(utf16);//b/f-Ne
var utf32 = encoding.utf16_to_utf32(utf16);
println(utf32);//b
utf8 = encoding.utf32_to_utf8(utf32);
println(utf8);//我是中文
```
## 5.4 hashlib
### ***class hashlib::md5(String strobj)***
MD5信息摘要算法,一種被廣泛使用的密碼散列函數,可以產生出一個128位(16字節)的散列值(hash value),用于確保信息傳輸完整一致。
構造函數相當于調用了update(strobj).
#### ***md5::update(String strobj)***
將strobj加入到MD5算法中.
*****
#### ***md5::hexdigest()***
輸出一個16字節的散列值換算成16進制的32位字符串
*****
#### ***md5::hexdigest16()***
取hexdigest()中第9-24位字符串生成16位字符串.
*****
#### ***md5::digest()***
輸出String類型16字節的散列值.(Hash value).
*****
#### ***md5::update_file(var filepath)***
講一個文件加入到MD5算法中.
*****
### ***class hashlib::sha1(String strobj)***
SHA-1可以生成一個被稱為消息摘要的160位(20字節)散列值,散列值通常的呈現形式為40個十六進制數
構造函數相當于調用了update(strobj).
#### ***sha1::update(String strobj)***
將strobj加入到SHA1算法中.
*****
#### ***sha1::hexdigest()***
輸出一個20字節的散列值換算成16進制的40位字符串
*****
#### ***sha1::digest()***
輸出String類型20字節的散列值.(Hash value).
*****
#### ***sha1::update_file(var filepath)***
將一個文件加入到SHA1算法中.
*****
### ***class hashlib::sha224(String strobj)***
SHA-2,名稱來自于安全散列算法2(英語:Secure Hash Algorithm 2)的縮寫,一種密碼散列函數算法標準,其中包括SHA-224、SHA-256、SHA-384、SHA-512.
構造函數相當于調用了update(strobj).
#### ***sha224::update(String strobj)***
將strobj加入到sha224算法中.
*****
#### ***sha224::hexdigest()***
輸出一個28字節的散列值換算成16進制的56位字符串
*****
#### ***sha224::digest()***
輸出String類型28字節的散列值.(Hash value).
*****
#### ***sha224::update_file(var filepath)***
將一個文件加入到sha224算法中.
*****
### ***class hashlib::sha256(String strobj)***
構造函數相當于調用了update(strobj).
#### ***sha256::update(String strobj)***
將strobj加入到sha256算法中.
*****
#### ***sha256::hexdigest()***
輸出一個32字節的散列值換算成16進制的64位字符串
*****
#### ***sha256::digest()***
輸出String類型32字節的散列值.(Hash value).
*****
#### ***sha256::update_file(var filepath)***
將一個文件加入到sha256算法中.
*****
### ***class hashlib::sha384(String strobj)***
構造函數相當于調用了update(strobj).
#### ***sha384::update(String strobj)***
將strobj加入到sha384算法中.
*****
#### ***sha384::hexdigest()***
輸出一個48字節的散列值換算成16進制的96位字符串
*****
#### ***sha384::digest()***
輸出String類型48字節的散列值.(Hash value).
*****
#### ***sha384::update_file(var filepath)***
將一個文件加入到sha384算法中.
*****
### ***class hashlib::sha512(String strobj)***
構造函數相當于調用了update(strobj).
#### ***sha512::update(String strobj)***
將strobj加入到sha512算法中.
*****
#### ***sha512::hexdigest()***
輸出一個64字節的散列值換算成16進制的128位字符串
*****
#### ***sha512::digest()***
輸出String類型64字節的散列值.(Hash value).
*****
#### ***sha512::update_file(var filepath)***
將一個文件加入到sha512算法中.
*****
```
#import "hashlib"
var md5 = new hashlib::md5();
md5.update("123456");
println(md5.hexdigest()); //e10adc3949ba59abbe56e057f20f883e
var sha1 = new hashlib::sha1();
sha1.update("123456");
println(sha1.hexdigest()); //7c4a8d09ca3762af61e59520943dc26494f8941b
var sha224 = new hashlib::sha224();
sha224.update("123456");
println(sha224.hexdigest());//f8cdb04495ded47615258f9dc6a3f4707fd2405434fefc3cbf4ef4e6
var sha256 = new hashlib::sha256();
sha256.update("123456");
println(sha256.hexdigest()); //8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92
var sha384 = new hashlib::sha384();
sha384.update("123456");
println(sha384.hexdigest());//0a989ebc4a77b56a6e2bb7b19d995d185ce44090c13e2984b7ecc6d446d4b61ea9991b76a4c2f04b1b4d244841449454
var sha512 = new hashlib::sha512();
sha512.update("123456");
println(sha512.hexdigest());//ba3253876aed6bc22d4a6ff53d8406c6ad864195ed144ab5c87621b6c233b548baeae6956df346ec8c17f5ea10f35ee3cbc514797ed7ddd3145464e2a0bab413
```
## 5.5 htmlparser
一個基礎的html解釋器.使用了http://github.com/rangerlee/htmlparser.git的開源項目.
### ***class htmlparser::parser***
#### ***parser::parse(var html)***
返回一個element對象root節點.如果解釋失敗則返回nil.
*****
### ***class htmlparser::element***
#### ***element::attr(var attrname)***
獲取當前標簽屬性值.
*****
#### ***element::children(var id)***
通過id屬性找到子層中的element對象.
*****
#### ***element::childrens_by_classname(var classname)***
通過class屬性找到子層中的element對象列表.
*****
#### ***element::childrens_by_tagname()***
通過tagname標簽找到子層中的element對象列表.
*****
#### ***element::childrens()***
獲取所有子層節點列表.
*****
#### ***element::select(var rule)***
根據規則篩選子層需要查找的element對象節點.并返回一個符合條件的列表.
```
rule 規則我也還沒太弄懂,等我稍后研究一下,在重新補文檔吧.或者可以聯系下作者.
```
*****
#### ***element::parent()***
獲取父節點element對象
*****
#### ***element::value()***
獲取當前節點中的值.如果存在.
*****
#### ***element::name()***
獲取當前節點的名字.
*****
#### ***element::text()***
將當前節點轉換為text文本格式
*****
#### ***element::html()***
取出當前節點內的html信息.
*****
## 5.6 http
### ***http.urlencode(String str)***
將字符串以URL編碼,用于編碼處理.
*****
### ***http.urldecode(String str)***
將字符串以URL解碼,用于解碼處理.
*****
### ***http.http_build_query(map data)***
用戶把map對象轉換成uri.
```
var data = {
a:10,
b:20
};
println(http.http_build_query(data));
結果:
a=10&b=20
```
*****
### ***class http::httprequest***
#### ***httprequest::setcookiefile(var cookisfilepath)***
設置cookie文件存放位置.
*****
#### ***httprequest::setproxy(var proxytype,String proxy)***
設置代理連接,proxy格式IP:PORT 例如127.0.0.1:12345
```
enum httpproxy
{
HTTP = 0,
HTTP1 = 1,
HTTPS = 2,
SOCKS4 = 4,
SOCKS5 = 5,
SOCKS4A = 6,
SOCKS5_HOSTNAME = 7
}
```
*****
#### ***httprequest::setproxyaccount(var account,var pwd)***
設置代理賬號密碼,如果需要的話.
*****
#### ***httprequest::addheaders(map hearders)***
增加需要發送的協議頭,格式為map.
```
//代碼示例.
httpreq.addheaders({
User-Agent: "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.25 Safari/537.36 Core/1.70.3776.400 QQBrowser/10.6.4212.400"
});
```
*****
#### ***httprequest::post(String url,String data)***
以POST形式發送一條http請求到url地址,并傳遞data內容.
*****
#### ***httprequest::get(String url)***
以GET形式發送一條http請求到url地址.
*****
#### ***httprequest::call(var reqeusttype,String url,String data)***
requesttype = 0 為Get方式
requesttype = 1 為Post方式 暫時未實現put delete方式,下個版本更新.
*****
#### ***httprequest::getcookies()***
獲取服務器返回新增的cookie列表.
*****
#### ***httprequest::getheaders()***
獲取服務器返回的HTTP協議頭信息.
*****
#### ***httprequest::settimeout(var sec)***
設置超時時間(單位秒) 默認為0永不超時.
*****
#### ***httprequest::followlocation(var isfollow)***
設置是否跟隨localtion跳轉.如果為了得到中間層時則需要設置不跳轉,根據header中的localtion信息手動跳轉.默認狀態為true跟隨跳轉.
*****
httprequest代碼示例
```
#import "http"
var request = new http::httprequest();
request.setcookiesfile("cookies.txt");
//request.setproxy(httpproxy.SOCKS5,"192.168.1.166:25455");
//request.setproxyaccount("johnsonyl","mima");
request.addheaders({User-Agent:"cppsrequest/1.1.0"});
var ret = request.get("http://127.0.0.1:8080/Home/test?kkk=100");
println("-------------------GET-------------------------");
println("get:{ret}");
var cookies = request.getcookies();
println("cookies:{cookies}");
var headers = request.getheaders();
println("headers:{headers}");
println("-------------------POST-------------------------");
ret = request.post("http://127.0.0.1:8080/Home/test","kkk=100");
println("post:{ret}");
var cookies = request.getcookies();
println("cookies:{cookies}");
var headers = request.getheaders();
println("headers:{headers}");
println("-------------------END-------------------------");
```
### ***class http::downloader***
可以跟蹤下載進度的下載器.
#### ***downloader::seturl(var url)***
設置要下載的url地址.
*****
#### ***downloader::setcookiefile(var cookiefile)***
設置cookie文件路徑.
*****
#### ***downloader::setfilepath(var filepath)***
設置要文件存放地址,注意如果地址因權限或者目錄不存在可能會導致下載失敗.
*****
#### ***downloader::download(var progresscallbackfunc)***
progresscallbackfunc 格式為 var progresscallbackfunc(var filesize,var size,var filecurrsize);
filesize 為文件總大小
size 為本次下載大小
filecurrsize 當前文件已下載大小.
調用次函數時才開始正式發起http請求開始下載文件.并根據網絡消息回調progresscallbackfunc函數.
*****
### ***class http::uploader***
一個模擬HTTP表單模式上傳器
#### ***uploader::addvalue(var name,var value)***
增加http表單參數.
*****
#### ***uploader::addfile(var name,var filepath,var fileminetype = "application/octet-stream")***
增加一個文件參數.
*****
#### ***uploader::setcookiefile(var cookiefile)***
設置cookie文件路徑.
*****
#### ***uploader::upload(var url,var progresscallbackfunc)***
progresscallbackfunc 格式為 var progresscallbackfunc(var now,vcar total,var speed)
now 為當前已上傳字節
total 為總上傳字節
speed 為浮點類型 每秒與服務器通信的字節速度.
*****
## 5.7 json
### ***json.encode(var v,var encodeingtype = json.encode_utf8)***
將一個map或者vector序列化成JSON字符串. 默認字符為UTF8字符格式.
*****
### ***json.decode(var jsonv)***
將一個json字符串反序列化成map或者vector.
*****
### ***json.encode_ascii = 2***
*****
### ***json.encode_unicode = 3***
*****
### ***json.encode_utf8 = 1***
*****
## 5.8 logging
此日志模塊提供了非常靈活的事件日志系統,使用標準庫中的日志模塊好處是所有的cpps模塊都可能參與日志輸出,包括你自己的日志消息和三方模塊的日志消息.
### ***logging.create(var config) 或者 logging.create_with_config(var config)***
通過配置來創建日志系統. CPPS去掉了基礎版日志 與 讀取配置文件模式的方式創建日志.作者認為就一個接口是最好的.
```
//config 是一個map對象. 主要由version,disable_existing_loggers,formatters,filters,handlers,loggers幾個參數組合而成.
//下面是一個示例
#import "logging"
var simple_format = '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s';
var standard_format = '[%(asctime)s][task_id:%(name)s][%(filename)s:%(lineno)d][%(levelname)s][%(message)s]';
var LOGGING_CONFIG = {
version: 1, // 版本號
disable_existing_loggers: false, // 固定寫法
formatters: {
standard: {
format: standard_format
},
simple: {
format: simple_format
},
},
filters: {},
handlers: {
//打印到終端的日志
sh: {
level: 'DEBUG',
class: 'logging.StreamHandler', // 打印到屏幕
color: [5,4,3,2,1] , // DEBUG,INFO,WARNING,ERROR,CRITICAL 顏色 1 紅色2 綠色3 黃色4.藍色5 紫色 6 青色
formatter: 'simple'
},
fh: {
level: 'DEBUG',
class: 'logging.handlers.TimedRotatingFileHandler', // 規則為:每5秒存一個文件. 可以根據自己需求修改.
formatter: 'standard',
filename: "log/log.txt",
mode: "ab+",
backupCount:5, //保留5個
when:"S", //秒
interval:5 //5秒
},
},
loggers: {
root: { //root 為默認
handlers: ['sh','fh'], //這里把上面定義的兩個handler都加上,即log數據既寫入文件又打印到屏幕
level: 'DEBUG',
propagate: true, // 向上(更高level的logger)傳遞
},
},
}
logging.create_with_config(LOGGING_CONFIG);
```
*****
### ***logging.debug(String logstr)***
向默認日志對象輸出DEBUG信息.
*****
### ***logging.info(String logstr)***
向默認日志對象輸出INFO信息.
*****
### ***logging.warning(String logstr)***
向默認日志對象輸出WARNING信息.
*****
### ***logging.error(String logstr)***
向默認日志對象輸出ERROR信息.
*****
### ***logging.critical(String logstr)***
向默認日志對象輸出CRITICAL信息.
*****
### ***logging.getlogger(String loggername)***
根據日志名字獲取日志對象
*****
### ***logging.GBK = 1***
*****
### ***logging.UTF8 = 2***
*****
### ***logging.UNICODE = 3***
*****
### ***logging.DEBUG = 10***
*****
### ***logging.INFO = 20***
*****
### ***logging.WARNING = 30***
*****
### ***logging.ERROR = 40***
*****
### ***logging.CRITICAL = 50***
*****
### ***class logging::Logger***
#### ***Logger::addhandler(class<logging.Handler> handler)***
向日志對象中添加新的日志接口.
*****
#### ***Logger::removehandler(class<logging.Handler> handler)***
移除一個日志處理接口.
*****
#### ***Logger::debug(String logstr)***
向該日志對象輸出DEBUG信息.
*****
#### ***Logger::info(String logstr)***
向該日志對象輸出INFO信息.
*****
#### ***Logger::warning(String logstr)***
向該日志對象輸出WARNING信息.
*****
#### ***Logger::error(String logstr)***
向該日志對象輸出ERROR信息.
*****
#### ***Logger::critical(String logstr)***
向該日志對象輸出CRITICAL信息.
*****
### ***class logging::Handler***
日志接口基類.并沒有更多相關實現.
*****
### ***class logging::StreamHandler***
向控制臺輸出流信息的日志接口.
#### ***StreamHandler::setformatter(String fmt)***
添加輸出日志的格式.
*****
#### ***StreamHandler::setcolor(vector colors)***
傳入一個數組來修改每個等級中日志的顏色信息.
數組中
0位代表DEBUG
1位代表INFO
2位代表WARNING
3位代表ERROR
4位代表CRITICAL
顏色 1 紅色2 綠色3 黃色4.藍色5 紫色 6 青色
```
handler.setcolor([5,4,3,2,1]); //DEBUG 為紫色.
```
*****
#### ***StreamHandler::setlevel(var level)***
設置輸出等級.
*****
### ***class logging::FileHandler***
向文件輸出日志接口.它會在目標文件最后追加日志,如果沒有管理日志它會無限增大文件大小.
#### ***FileHandler::setformatter(var fmt)***
添加輸出日志的格式.
*****
#### ***FileHandler::setfile(var filepath)***
設置日志文件的路徑.
*****
#### ***FileHandler::setdelay(var isdelay)***
暫時沒有作用.
*****
#### ***FileHandler::setlevel(var level)***
設置輸出等級.
*****
### ***class logging::RotatingFileHandler***
向文件輸出日志接口.與FileHandler不同的是它有setmaxbytes接口.在大于設置閾值時系統將通過為原文件名添加擴展名 '.1', '.2' 等來保存舊日志文件。 例如,當 backupCount 為 5 而基本文件名為 app.log 時,你將得到 app.log, app.log.1, app.log.2 直至 app.log.5。 當前被寫入的文件總是 app.log。 當此文件寫滿時,它會被關閉并重戶名為 app.log.1,而如果文件 app.log.1, app.log.2 等存在,則它們會被分別重命名為 app.log.2, app.log.3 等等。因此你通常要設置 backupCount 至少為 1,而 maxBytes 不能為零。 否則會造成不能發生輪換.
#### ***RotatingFileHandler::setformatter(var fmt)***
添加輸出日志的格式.
*****
#### ***RotatingFileHandler::setfile(var filepath)***
設置日志文件的路徑.
*****
#### ***RotatingFileHandler::setdelay(var isdelay)***
暫時沒有作用.
*****
#### ***RotatingFileHandler::setlevel(var level)***
設置輸出等級.
*****
#### ***RotatingFileHandler::setmaxbytes(var maxbytes)***
單個文件最大字節數.
*****
#### ***RotatingFileHandler::setbackupcount(var backupcount)***
設置備份日志數量.
*****
### ***class logging::TimeRotatingFileHandler***
向文件輸出日志接口.與FileHandler不同的是它有when接口.在when條件時系統將通過為原文件名添加擴展名 '.1', '.2' 等來保存舊日志文件。 例如,當 backupCount 為 5 而基本文件名為 app.log 時,你將得到 app.log, app.log.1, app.log.2 直至 app.log.5。 當前被寫入的文件總是 app.log。 當此文件寫滿時,它會被關閉并重戶名為 app.log.1,而如果文件 app.log.1, app.log.2 等存在,則它們會被分別重命名為 app.log.2, app.log.3 等等。因此你通常要設置 backupCount 至少為 1,而 when 條件不能為空。 否則會造成不能發生輪換.
#### ***TimeRotatingFileHandler::setformatter(var fmt)***
添加輸出日志的格式.
*****
#### ***TimeRotatingFileHandler::setfile(var filepath)***
設置日志文件的路徑.
*****
#### ***TimeRotatingFileHandler::setdelay(var isdelay)***
暫時沒有作用.
*****
#### ***TimeRotatingFileHandler::setwhen(String when,var interval)***
設置when輪換條件.
| 值 | 間隔類型 | 如果/如何使用*when* |
| --- | --- | --- |
| `'S'` | 秒 | 忽略 |
| `'M'` | 分鐘 | 忽略 |
| `'H'` | 小時 | 忽略 |
| `'D'` | 天 | 忽略 |
| `'W0'-'W6'` | 工作日(0=星期一) | 用于計算初始輪換時間 |
| `'midnight'` | 如果未指定*atTime*則在午夜執行輪換,否則將使用*atTime*。 | 用于計算初始輪換時間 |
*****
#### ***TimeRotatingFileHandler::setbackupcount(var backupcount)***
設置備份日志數量.
*****
#### ***TimeRotatingFileHandler::setlevel(var level)***
設置輸出等級.
*****
## 5.9 mysql
### ***mysql.connect(class<MysqlOption> option)***
創建一個mysql連接. 如果連接成功則返回class<mysql::mysql> ,如果連接失敗則返回nil.
下面可以看一下代碼示例.
```
在bin/lib/mysql/main.cpp中有option的類定義
class MysqlOption
{
var db;
var host;
var user;
var passwd;
var port;
var isreconnect;
}
var conn = mysql.connect(new MysqlOption(){
db = "testdb",
host = "127.0.0.1",
user = "root",
passwd = "123456",
port = 3306,
isreconnect = true
});
if(conn == null)
println("mysql connected faild.");
```
*****
### ***class mysql::mysql***
此類是對libmysqlclient for c進行了封裝.使用了mysql_stmt進行存儲過程操作,所以不存在注入風險.可以放心使用.建議盡量或者不要使用execute語句執行.因為這是非常不安全的.
#### ***mysql::close()***
關閉mysql連接.
*****
#### ***mysql::call(String procname,vector params)***
調用存儲過程,procname 為存儲過程名稱,vector對應參數列表.需要與存儲過程中參數順序一致.并不需要名稱.
call會返回一個vector<class<mysql_result>> 的列表.mysql可以調用多個select語句并且返回多個結果集.每個結果集中包含多條record數據.
*****
#### ***mysql::execute(var sqlcmd)***
調用sql語句. sql語句存在注入風險,并不推薦使用.
*****
#### ***mysql::error()***
返回最后一次執行call或execute時mysql返回的錯誤碼.
*****
#### ***mysql::isconnect()***
檢測是否連接陳宮
*****
#### ***mysql::affected_rows()***
返回最后一次執行execute或call調用update 與 insert的影響行數.
*****
### ***class mysql::mysql_record***
#### ***mysql_record::fields()***
獲取所有字段信息. 返回類型為map. 可以根據fields實例化對象,或者打印查看返回結果.
*****
#### ***mysql_record::get(var name)***
根據列名稱獲取本行的值.
*****
### ***class mysql::mysql_result***
#### ***mysql_result::records()***
獲取當前result中所有的行數據.
*****
MYSQL模塊代碼示例
```
#import "mysql"
var option = new MysqlOption()
{
db = "test",
host = "127.0.0.1",
user = "root",
passwd = "123456",
prot = 3306,
isreconnect = true
}
var conn = mysql.connect(option);
if(conn == null)
{
println("connect error");
}
var results = conn.call("selectuser",["johnson"])
println(conn.error());
foreach(var result : results)
{
foreach(var record : result.records())
{
println("----------------------------------------");
foreach(var field: record.fields())
{
println("{field.first()}:{field.second()}");
}
}
}
var results = conn.execute("select * from users where username = 'johnson';")
println(conn.error());
foreach(var result : results)
{
foreach(var record : result.records())
{
println("id:"..record.get("id"));
println("username:"..record.get("username"));
println("userpwd:"..record.get("userpwd"));
}
}
```
## 5.10 quoteprintable
它是多用途互聯網郵件擴展(MIME) 一種實現方式。其中MIME是一個互聯網標準,它擴展了電子郵件標準,致力于使其能夠支持非ASCII字符、二進制格式附件等多種格式的郵件消息。目前http協議中,很多采用MIME框架!quoted-printable 就是說用一些可打印常用字符,表示一個字節(8位)中所有非打印字符方法!
### ***quotedprintable.encode(String str)***
quotedprintable方式編碼.
*****
### ***quotedprintable.decode(String str)***
quotedprintable方式解碼.
*****
## 5.11 smtp
### ***stmp.makeboundary()***
系統自帶生成boundary格式 源碼在bin/lib/smtp/main.cpp中.
示例:
```
#import "smtp"
println(smtp.makeboundary());
結果:
__977_NextPart_44420295_=----
```
*****
### ***stmp.encodeutf8text(var str)***
smtp格式使用base64編碼.
代碼示例
```
#import "smtp"
println(smtp.encodeutf8text("helloworld"));
結果:
=?UTF-8?B?aGVsbG93b3JsZA==?=
```
*****
### ***stmp.sendmail(var opt)***
一個簡單的發送郵件功能.
代碼示例
```
#import "smtp"
//基礎版本 夠用
var code = smtp.sendmail({
host:"smtp.cppscript.org", //必填
port:25, //必填
username:"********@cppscript.org", //必填
pwd:"********", //必填
content:"<a>hello</a>", //必填
subject:"hello", //必填
messageid:"{time.gettime()}@cppscript.org", //按這個格式換下
to:[
["88481106@qq.com","名字"], //2種格式
],
onread:[](var s){
println(s);
}
});
println(code);
//旗艦版本
// smtp.sendmail({
// host:"smtp.cppscript.org", //必填
// port:25, //必填
// username:"********@cppscript.org", //必填
// pwd:"********", //必填
// ssl:false,
// timeout:30,
// mailfrom:"********@cppscript.org",
// from:"********@cppscript.org",
// sender:"johnson",
// content:"<a>hello</a>", //必填
// subject:"hello", //必填
// verbose:true, //是否輸出調試信息
// encoding:2, // 0.8bit 1.base64 2.quoted-printable
// boundary:smtp.makeboundary(),
// messageid:"{time.gettime()}@cppscript.org", //按這個格式換下
// charset:"UTF-8",
// tls:false,
// to:[
// ["88481106@qq.com","名字"], //2種格式
// "14827@qq.com"
// ],
// cc:[], //抄送
// bcc:[], //密送
// headers:{
// GUID:"MakeGUID", //自定義headers
// },
// attachments:{
// aaa.txt:{
// type:"text/plain", //附件
// id:"content-id-test",
// content:"aaaa",
// },
// bbb.txt:{
// type:"application/x-msdownload", //附件
// id:"content-id-test",
// content:"bbbb",
// }
// }
// });
```
*****
### ***class smtp::smtpclient***
當標準庫提供的sendmail不能符合業務需求時可以通過smtpclient自定義發送郵件.
#### ***smtpclient::setsmtp(var host,var port)***
設置smtp地址與端口
*****
#### ***smtpclient::setmailfrom(var mailfrom)***
設置mail from信息
*****
#### ***smtpclient::setuserinfo(var username,var passwd)***
設置賬號信息,如果需要的話.不設置則會使用直投方式.
*****
#### ***smtpclient::openssl(var isssl)***
是否開啟ssl
*****
#### ***smtpclient::addrecipient(var rcptto)***
添加收件人地址
*****
#### ***smtpclient::setverbose(var verbose)***
是否開啟調試信息.
*****
#### ***smtpclient::settimeout(var sec)***
設置郵件超時時間單位秒
*****
#### ***smtpclient::setreadcallback(var onreadfunc)***
設置服務器回信回調函數,回調函數原型 var onreadfunc(var msg);
*****
#### ***smtpclient::setcontent(var content)***
設置郵件詳細內容.包含郵件頭+內容+附件等信息.
*****
#### ***smtpclient::send()***
開始發送.
*****
## 5.12 socket
本擴展模塊是對libevent的封裝,Libevent 是一個用C語言編寫的、輕量級的開源高性能事件通知庫,主要有以下幾個亮點:事件驅動( event-driven),高性能;輕量級,專注于網絡,不如 ACE 那么臃腫龐大;源代碼相當精煉、易讀;跨平臺,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多種 I/O 多路復用技術, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定時器和信號等事件;注冊事件優先級。本模塊主要封裝了Server,Client,HttpServer服務.
### ***class socket::server***
本類主要封裝了socket中的server服務.作者特別在option里面增加自定義包頭大小.和包頭parser解析接口.使用它可以輕松簡單的解決斷包,黏包等問題.
#### ***server::setoption(class<ServerOption> option)***
```
ServerOption的定義在bin/lib/socket/main.cpp中.
class ServerOption
{
var ip; //綁定 IP.
var accept; //用戶連接回調函數.
var data; //用戶消息到達回調函數
var close; //用戶關閉連接回調函數
var parser; //自定義協議頭解析回調函數
var headersize; //自定義協議頭大小
}
```
*****
#### ***server::listen(var port)***
開啟監聽服務.參數為監聽端口號.
*****
#### ***server::run()***
事件驅動接口,需要用戶調用run來驅動server的所有事件觸發.
*****
#### ***server::send(var socketIndex,Buffer buf)***
發送給指定客戶端,Buffer為數據流.為了更好的使用socket這里沒有使用String.
*****
#### ***server::closesocket(var socketIndex)***
關閉一個客戶端連接.
*****
#### ***server::stop()***
停止server服務.
*****
#### ***server::isrunning()***
返回server服務的運行狀態.
*****
server服務的代碼示例.
```
#import "socket"
//服務端部分
var socket_accept(var socketIndex)
{
println("檢測到有一個連接:{socketIndex}");
}
var socket_data(var socketIndex,var buffer)
{
var packageSize = buffer.readInt32();
var s = buffer.readString(packageSize-4);
println(s);
if(s == "e")
{
srv.closesocket(socketIndex);
}
var writer = new Buffer();
writer.writeString(s);
socket_send(socketIndex, writer);
}
var socket_close(var socketIndex,var err,var errstr)
{
println("檢測到有一個關閉:{socketIndex},err:{err},errstr:{errstr}");
}
var socket_parser(var headerbuffer)
{
var size = headerbuffer.readInt32();
return size;
}
var socket_send(var socketIndex,var buffer)
{
var writer = new Buffer();
writer.writeInt32(buffer.length() + 4);
writer.write(buffer,buffer.length());
srv.send(socketIndex, writer);
}
println("start server");
var srv = new socket::server().setoption(new ServerOption(){
ip = "0.0.0.0",
headersize = 4,//字節
accept = socket_accept,
data = socket_data,
close = socket_close,
parser = socket_parser
}).listen(4060);
println("start over");
while (true){
srv.run();
Sleep(1);
}
```
### ***class socket::client***
本類主要封裝了socket中的client服務.作者特別在option里面增加自定義包頭大小.和包頭parser解析接口.使用它可以輕松簡單的解決斷包,黏包等問題.
#### ***client::setoption(class<ClientOption> option)***
```
ClientOption 定義在/lib/socket/main.cpp中.
class ClientOption
{
var connected; //客戶端連接完成回調函數
var data; //服務端消息到達回調函數
var close; //客戶端連接斷開回調函數
var parser; //自定義協議頭解析回調函數
var headersize; //自定義協議頭大小
}
```
*****
#### ***client::run()***
事件驅動接口,需要用戶調用run來驅動client的所有事件觸發.
*****
#### ***client::send(Buffer buf)***
發送數據給服務端,Buffer為數據流.為了更好的使用socket這里沒有使用String.
*****
#### ***client::closesocket()***
關閉與服務端的連接.
*****
#### ***client::connect(var ip,var port)***
開啟一個對ip:port的連接.如果連接成功則返回true.失敗則返回false. 由于是異步連接,真是連接狀態應在connected回調函數中做詳細判斷.
*****
#### ***client::isconnect()***
返回連接狀態.
*****
### ***class socket::httpserver***
httpserver 是一個非常強大的基于MVCT框架的Http服務,它有controller,Model,View,與Template.并且它有cookie,session,cache等網站系統應有的服務.您可以使用cpps訂制自己需要的API接口甚至是一套網站.
本文檔只是簡單介紹一下接口,如果想了解更多httpserver中的使用方法,可以查看更詳細的Httpserver使用.
#### ***httpserver::setoption(class<HttpServerOption> option)***
```
HttpServerOption 定義在/lib/socket/main.cpp中.
class HttpServerOption
{
var ip; //綁定IP
var exceptionfunc; //當腳本運行總發生異常時的回調函數
var notfoundfunc; //當腳本找不到路由時,通常講為404時的回調函數.
}
```
*****
#### ***httpserver::listen(var port)***
開啟監聽服務.參數為監聽端口號.
*****
#### ***httpserver::add_type(var mime,var ext)***
添加支持的MimeType類型.支持的MimeType類型就可以跟IIS或者apache一樣通過路徑訪問到.
mime為Mime-Type,ext為擴展名.
代碼示例
```
add_type("text/html","html");
```
*****
#### ***socket.initmimetype(class<httpserver> srv)***
我們提供了一個基本上支持大部分mimetype的初始化函數.但是里面的缺點就是太全了.有一些并不想支持的也支持了.所以可以根據自己的需要從里面提取自己需要支持的mimetype.
*****
#### ***httpserver::get_type(var ext)***
根據擴展名獲取已支持的mime-type.
*****
#### ***httpserver::run()***
事件驅動接口,需要用戶調用run來驅動httpserver的所有事件觸發.
*****
#### ***httpserver::register_handlefunc(var path,var func)***
重寫路由中一個路徑.
*****
#### ***httpserver::register_controller(var __class,var defaultset = false)***
注冊controller,Controller會根據用戶訪問路徑找到對應Controller 并創建一個對象訪問其函數.這里有一點就是對函數名稱的大小寫是敏感的.所以在類中的函數名是大寫則訪問地址為大寫,小寫則為小寫. 類名則不敏感.例如以下示例
```
//類名則對路徑不敏感.
class Home
{
//對應訪問地址則是http://xxx/home/Index
var Index(var request)
{
}
//對應訪問地址則是http://xxx/home/index
var index(var request)
{
}
}
```
*****
#### ***httpserver::stop()***
停止httpserver服務.
*****
#### ***httpserver::isrunning()***
獲取httpserver服務運行狀態
*****
### ***class socket::httprequest***
httprequest是每次請求時對象的一個實體,可以通過它獲取用戶發送的協議頭,參數,cookie以及session等信息.也是通過它返回信息給瀏覽器.
#### ***httprequest::addheader(map headers)***
添加返回協議頭,header為map,分別通過kv方式添加.
*****
#### ***httprequest::append(String str)***
向返回數據流中添加數據.
*****
#### ***httprequest::send(var code,var msg)***
將數據流返回給瀏覽器.code 為返回碼,正確為200,msg為OK,或者 302 跳轉一類的.可以通過搜索引擎搜索HTTP狀態碼獲取更多更詳細的介紹..
*****
#### ***httprequest::getparam(var name)***
不區分get與post獲取參數.
*****
#### ***httprequest::get(var name)***
只獲取get部分的參數
*****
#### ***httprequest::post(var name)***
只獲取post部分的參數
*****
#### ***httprequest::getheader(var name)***
獲取瀏覽器發送來的協議頭.
*****
#### ***httprequest::geturi()***
獲取瀏覽器訪問時的URI部分.
*****
#### ***httprequest::getpath()***
獲取瀏覽器訪問時的path部分.
*****
#### ***httprequest::getbuffer()***
當用戶以POST形式訪問時,獲取post的整體數據部分.
*****
#### ***httprequest::setcookie(var key,var value, var path = "/",var domain = "",var max_age = nil)***
添加cookie,并返回給瀏覽器Set-Cookie
*****
#### ***httprequest::getcookie(var key)***
獲取瀏覽器返回的cookie參數.
*****
#### ***httprequest::getfiledata(var name)***
當瀏覽器以表單形式上傳文件,則需要使用getfiledata獲取文件流.
*****
#### ***httprequest::paramslist()***
獲取所有參數列表包含GET與POST.
*****
#### ***httprequest::getlist()***
獲取GET方式中的所有參數.
*****
#### ***httprequest::postlist()***
獲取POST方式中的所有參數.
*****
#### ***httprequest::session()***
獲取SESSION對象
*****
### ***class socket::filedata***
#### ***filedata::name()***
返回參數名稱
*****
#### ***filedata::filename()***
返回文件名稱
*****
#### ***filedata::content_type()***
返回Mime-Type類型.
*****
#### ***filedata::getbuffer()***
返回文件數據流,String類型.
*****
### ***class socket::httpsession***
SESSION暫時只實現了一種內存型緩存機制.當程序被關閉則SESSION會全部失效.后續會開發db或者file版本的SESSION.以保證SESSION可以持久化.
#### ***httpsession::get(var key,var defaultval = "")***
獲取session中的值.當不存在會添加并設置defaultval值.
*****
#### ***httpsession::set(var key,var value)***
設置session中的值
*****
#### ***httpsession::clear()***
清空session中的所有值.
*****
#### ***httpsession::remove()***
移除session.下次訪問的時候會創建新的session對象.
*****
## 5.13 uuid
在WINDOWS中稱之為GUID,在UNIX系統中稱之UUID.
### ***class uuid::UUID***
UUID對象.
#### ***UUID::tostring()***
將UUID對象轉為字符串
*****
### ***uuid.create()***
創建一個UUID對象.
*****
### ***uuid.uuid4()***
創建一個UUID對象并直接轉為字符串輸出.(最基礎的UUID生成.)
*****
# 6.如何嵌入CPPS到自己程序
嵌入cpps非常簡單.只需要包含一個頭文件#include <cpps/cpps.h>即可.
下面代碼示例 包括嵌入cpps,并且一些cpps與c++調用的示例.
```
#include <iostream>
#include <cpps/cpps.h>
using namespace cpps;
using namespace std;
namespace cpps { std::string cpps_io_getfilepath(std::string str);std::string cpps_rebuild_filepath(std::string path);std::string cpps_real_path(); }
class CppClassTest
{
public:
void testFunc(cpps::object val)
{
if (val.isint())
{
printf("val:%d\r\n", object_cast<int32>(val));
}
printf("%s:%s\r\n", "CppClassTest::testFunc",type_s(val).c_str());
}
};
int32 main(int argc,char **argv)
{
std::string path = "main.cpp";
if (argc >= 2) {
path = argv[1];
}
path = cpps_rebuild_filepath(path);
#ifdef WIN32
SetCurrentDirectoryA(cpps_io_getfilepath(path).c_str());
#else
if (chdir(cpps_io_getfilepath(path).c_str())) {}
#endif
C* c = cpps::create(argc,argv);
cpps::_module(c)[
_class<CppClassTest>("CppClassTest") //注冊CPP類給cpps中.
.def("testFunc",&CppClassTest::testFunc)
];
_CPPS_TRY
cpps::dofile(c, path.c_str());
_CPPS_CATCH;
/*
// script page
class Agent
{
var val;
var test()
{
println(val);
}
}
var a = [1,2,3,4,1000];
var b = { a:1 , b:2 ,c:3};
*/
//loop vector
cpps::object a = cpps::object::globals(c)["a"];
for (cpps::object v : cpps::object::vector(a))
{
cpps_integer val = v.toint();
cout << val << endl;
}
//print vector a value
cpps::object vv = a[4];
cout << "a[4] = " << object_cast<cpps_integer>(vv) << endl;
a.set(4, cpps::object::create(c, 2000));
vv = a[4];
cout << "a[4] = " << object_cast<cpps_integer>(vv) << endl;
cpps::object::vector vec(a);
vec[4] = cpps::object::create(c, 3000);
cout << "a[4] = " << object_cast<cpps_integer>(vec[4]) << endl;
//////////////////////////////////////////////////////////////////////////
//loop map
cpps::object b = cpps::object::globals(c)["b"];
for (auto v : cpps::object::map(c,b))
{
std::string key = object_cast<std::string>(v.first); // object_cast or std::string key = object(v.first).tostring();
cpps_integer val = cpps::object(v.second).toint();
cout << key << ":" << val << endl;
}
//print map a value.
cpps::object bb = b["b"];
cout << "b['b'] = " << object_cast<cpps_integer>(bb) << endl;
b.set("b", cpps::object::create(c, 2000)); // It's Work.
bb = b["b"];
cout << "It's Work b['b'] = " << object_cast<cpps_integer>(bb) << endl;
b.set("z", cpps::object::create(c, 2000)); // It doesn't work. Because z doesn't exist
cpps::object bz = b["z"];
cout << "doesn't work b['z'] type = " << cpps::type_s(bz) << endl;
cpps::object key = cpps::object::create(c, "z");
b.set(key, cpps::object::create(c, 2000)); // It's Work. z must be created.
bz = b["z"];
cout << " It's Work b['z'] = " << object_cast<cpps_integer>(bz) << endl;
cpps::object::map bmap(c, b);
bmap["z00"] = cpps::object::create(c, "hello world");
cout << "bmap-> It's Work bmap['z00'] = " << object_cast<std::string>(bmap["z00"]).c_str() << endl;
bmap[1] = cpps::object::create(c, "interge key it's work too");
cout << "bmap-> It's Work bmap[1] = " << object_cast<std::string>(bmap[1]).c_str() << endl;
//////////////////////////////////////////////////////////////////////////
//defined a global var;
cpps::object::define(c, "x", cpps::object::create(c, "hello world"));
cout << "x = " << object_cast<std::string>(cpps::object::globals(c)["x"]).c_str() << endl;
cpps::object::globals(c)["x"] = cpps::object::create(c, "wonderful world");
cout << "x = " << object_cast<std::string>(cpps::object::globals(c)["x"]).c_str() << endl;
_G(c)["x"] = cpps::object::create(c, "_G get var it's work.");// work too
cout << "x = " << object_cast<std::string>(_G(c)["x"]).c_str() << endl;
//////////////////////////////////////////////////////////////////////////
//new script class var ,and set dofunction.
cpps::object Agent = cpps::object::globals(c)["Agent"];
if (Agent.isclass())
{
cpps::object Agentvar = cpps::object::create_with_cppsclassvar(c, Agent);
Agentvar.set("val", cpps::object::create(c, "this is string.") );
cpps::object testfunc = Agentvar["test"];
if (testfunc.isfunction())
{
cpps::doclassfunction(c, Agentvar,testfunc);
}
Agentvar["val"] = cpps::object::create(c, "change string value.");
if (testfunc.isfunction())
{
cpps::doclassfunction(c, Agentvar, testfunc);
}
}
//////////////////////////////////////////////////////////////////////////
//new cpp class var
CppClassTest* ptr = NULL; //You don't need to delete it. it has GC to delete it.
cpps::object cppclassvar = cpps::object::create_with_classvar< CppClassTest >(c, &ptr);
//ptr->testFunc(xxx); //you can do something in cpp .
cpps::object testFunc = cppclassvar["testFunc"];
if (testFunc.isfunction()) {
cpps::doclassfunction(c, cppclassvar, testFunc); //or get function and call it.
}
if (testFunc.isfunction()) {
cpps::doclassfunction(c, cppclassvar, testFunc,cpps::object::create(c,123456)); //or get function and call it.
}
if (testFunc.isfunction()) {
cpps::doclassfunction(c, cppclassvar, testFunc,cpps::object::create(c,"asdfsadf")); //or get function and call it.
}
cpps::close(c);
return 0;
}
```
# 7.如何編寫模塊給CPPS
編寫模塊給cpps非常簡單.只需要實現cpps_attach與cpps_detach接口就可以了.
源碼在example\demo中.
```
#include <cpps/cpps.h> //Single Header
using namespace cpps;
void demotest()
{
printf("hit demo function.\r\n");
}
cpps_export_void cpps_attach(cpps::C* c)
{
printf("attach demo\r\n");
cpps::cpps_init_cpps_class(c); //init cpps module
cpps::_module(c)[
def("demotest", demotest)
];
}
cpps_export_void cpps_detach(cpps::C* c)
{
printf("detach demo\r\n");
}
cpps_export_finish //unix export.
```