文件读写及网络传输举例
在鲁班软件包里包括一些常用的对象类型和部件用于文件和网络的数据输入和输出. 虽然严格说来这些常用工具并不是鲁班语言的组成部分, 就象printf函数不是C语言的一部分一样. 但是所有C语言的书都会讲printf. 我们在这里也专用一章讲这些实用的类型和部件.
9.1 终端, 文件和网络SOCKET
这三个对象类型都用于数据输入输出, 而且它们的界面基本一样. 以下讲述它们的应用细节及举例.
鲁班类型名:
终端: std::console
文件: std::file
网络Socket: net::socket
以上三个对象类型共同的成员函数列举如下.
write(obj1,obj2
)
将对象逐个转化成字符串然后印出或传出
返回空值null, 如果操作失败则返回错误类型
writeline(obj1,obj2
)
将对象逐个转化成字符串然后印出或传出, 每个对象后附加一个换行符.
返回空值null, 如果操作失败则返回错误类型
read( int n=1 )
读入指定数量(n)的字符. 如果n没有指定则读一个字符.
返回读取的字符串. 如果操作失败则返回错误类型.
readline()
逐个读取字符一直到碰到一个换行符.
返回读取的字符串. 如果操作失败则返回错误类型.
writeobj(obj1,obj2
)
将对象逐个转化成独立于硬件及操作系统的串行流然后写出或传出
返回空值null, 如果操作失败则返回错误类型
readobj()
读取串行流内容, 并将串行流恢复成数据对象. 一般情况是串行流是
以前的writeobj操作产生.
返回所恢复的数据对象.
一般来说, write和writeline用来把对象写成字符串显示来给人读. 而writeobj和readobj用来在程序或机器之间传递数据. 不管简单或复杂的数据对象, 鲁班语言保证用writeobj写出的对象一定可以用readobj来读回来. 而且写和读可以在不同的程序甚至机器上.
9.2 文件读写举例
一个一般文字处理的例子:
linecount = 0;
wordcount = 0;
txtfile = std::file(mydatafile,
r); // 打开一个叫mydatafile的文件
alldata = txtfile.readall();
// 读取全部文件内容到alldata变量
lines = alldata.split(\n); // 将alldata分行到变量lines
foreach(
line in lines ) // 枚举在lines里的每一行
{
++linecount; //增加行数
words = line.split(); // 将每行分成单词数组words
wordcount += words.size();
// 增加单词数
}
//下面打印结果
std::println(obj=lines: +string(linecount)+
words: +string(wordcount));
以上程序打开一个叫mydatafile的文件, 将文件全部内容读到一个叫alldata的变量里. 然后将内容分行, 将每行再分成单词, 数行数和单词数. 最后将结果打印在屏幕上. 请注意以上代码用了一个新的成员函数readall()这个函数一次将文件的所有内容读出成一个字符串返回.
下面例子代码演示怎样将数据对象以串行流格式写入文件, 然后怎样再从文件恢复数据对象.
fw = std::file(objfile, w); // 打开文件准备写
fw.writeobj([1,2,3]); // 一次将一个数组转化成串行流写入文件
fw.close(); // 关闭文件
fr = std::file(objfile, r); // 打开文件准备读
vec = fr.readobj();
// 将以前写入的数组串行流读出并恢复成对象
std::println(obj=vec); // 打印 [1,2,3]
一个文件可以用三种状态打开, 读状态, 写状态和添加状态, 分别由三个字符r, w, a
表示. 读状态只能打开已经存在的文件. 试图用读状态打开一个不存在的文件会返回错误. 写状态会创建文件, 如果文件已经存在, 其内容会被清除. 添加状态也会创建文件, 但如果文件已经存在, 其内容不会被清除, 新的内容被添加到文件尾.
9.3 网络读写举例
以下鲁班例子代码演示怎样使用网络SOCKET来实现一个简单的客户-服务器之间的数据传输. 以下例子使用了一个新的数据类型net::listener. 这个数据类型用于网络通讯的服务器一边. net::listener的对象创建时的输入参数是一个TCP端口号.它的主要成员函数是accept(). 这个成员函数会在TCP端口等待客户从网络上来的连接请求, 收到后返回一个net::socket对象. 用这个SOCKET对象就可以象读写文件那样和客户对话.
服务器脚本:
//
此服务器问候客户并告状客户排号是第几
listener = net::listener(6500); // 服务器的TCP端口号是6500
clientcount=0;
while( true
) // 循环
{
socket
= listener.accept();
// 等待接收新的客户连接
name = socket.readobj(); // 读客户名字
socket.writeobj(Hello,
+name); // 问侯客户
++clientcount; // 序号加一
Socket.writeobj(clientcount); // 将序号送给客户
}
客户脚本:
socket = net::socket(localhost, 6500); // 连接服务器端口6500
socket.writeobj(Chinese); // 告诉服务器自己名字
msg = socket.readobj(); // 接收服务器发的问候
std::console().writeline(The server says: , msg); // 打印问候
mynumber = socket.readobj();
//接收服务器发的序号
std::console().writeline(My number is
, mynumber); // 打印序号
以上两个鲁班程序, 一个是服务器程序, 一个是客户程序. 服务器程序打开一个TCP端口6500, 然后等待客户连接. 收到连接后先读客户送来的名字, 然后发送问候字符串, 最后将客户连接的序号发给客户. 客户程序的读写顺序则相反. 客户先创建一个SOCKET连接到服务器的6500 TCP端口. 然后将自己名字发给服务器. 再读问候字符串并打印出来. 最后读服务器发来的序号也打印出来.
这个例子程序里服务器和客户在同一个机器上运行. 在客户程序里写的服务器名字是localhost. 运行时先启动服务器程序, 然后启动客户程序. 如果服务器和客户在不同的机器上, 只需修改客户程序, 将localhost改为服务器机器的名字就好.
为了客户方便, 鲁班软件包包括标准打印部件std::println std::print 这两个部件把输入的对象打印到屏幕上. 它们都是用鲁班语言写成. 内部用的是std::console 对象类型. 以下是它们的源代码.
namespace std;
struct println(
input obj;)
as process
{
std::console().writeline(input.obj);
}
struct print( input obj;)
as process
{
std::console().write(input.obj); }
所有的鲁班数据对象或部件都可以转换成串行流字符串. std::des是鲁班的一个标准部件用来从串行流字符串恢复原来的数据对象. 以下是代码例子.
x =
{one:1, two:2}; //创建一个字典对象
xguts = x.serialize();// 转换成串行流字符串
xback = std::des(stream=xguts).obj; // 调用std::des恢复对象
truth = xback == x; // truth = true
以上代码创建一个字典对象然后转换成串行流字符串, 再调用std::des部件来从串行流恢复原来的对象. 最后比较恢复的对象和原本对象看它们是否一样.