“
在CAPL学习之路的系列文章中,提及过capl读写文件的相关函数,今天介绍几种capl可读写的文件类型,用到的函数接口如下
”
众所周知,要把大象装冰箱,总共分三步:把冰箱门打开,把大象装进去,把冰箱门关上
串台了串台了,重来,想要读写文件,总共也分三步:打开文件,读写内容,关闭文件
根据这种思路,我们一起试着读/写文件内容
txt文件
读取文件
首先在电脑上创建一个test.txt文件,输入一些内容后保存
接下来根据三步走的思路,来实现读取文件内容
打开文件
用openFileRead函数打开文件,它是以read access的方式打开文件的
传入的参数有两个是必需的,filename是文件的路径,mode是以何种方式打开
文件路径可以是绝对路径,也可以是相对路径,绝对路径没什么可说的,相对路径就是必须把文件放入CANoe工程文件的目录下
如果是和CANoe工程同级目录,就可以直接使用test.txt文件名,如果放在次级目录下,比如放在CANoe工程目录的Files文件夹里,传入的filename就是”Filestest.txt”
当然你也可以获取CANoe工程目录里的任意文件的绝对路径
on key 'a'
{
char absPath[256];
getAbsFilePath("Files\test.txt", absPath, elcount(absPath));
write("absPath: %s", absPath);
}
参数mode为0,表示文件是以text模式打开的
参数mode为1,表示文件是以binary模式打开的
txt文件,肯定以text模式打开啦
函数openFileRead还有返回值,它是打开的文件的句柄,后面读写文件内容的操作都是用这个句柄调用读写函数来执行的
读取文件
txt文件的内容是字符串类型的,所以它的读函数可以用下面这两个函数
那么它们的区别是什么呢,我们来一个一个地试验
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
glbHandle = openFileRead("Files\test.txt", 1);
if (glbHandle != 0) //打开成功
{
fileGetString(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
}
}
打印的结果为
我们再把fileGetString换成fileGetStringSZ
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
glbHandle = openFileRead("Files\test.txt", 1);
if (glbHandle != 0) //打开成功
{
fileGetStringSZ(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
}
}
打印的结果为
首先可以确定一点
“
无论是fileGetString还是fileGetStringSZ,只调用一次,并不能读取整个txt文件的内容,它们只能读取一行内容
”
看起来它们之间还有点区别,通过查看它们的用法,发现调用fileGetString读取的内容会包含换行符,而fileGetStringSZ并不会包含换行符,这也就解释了为什么前者的打印结果中多了一行空白
我们知道,对于windows系统来说,采用回车加换行的方式来实现换行,也就是”rn”,所以fileGetString读取的一行中包括”rn”,fileGetStringSZ读取的一行中包括”r”
既然打印不出转义字符,我们可以在代码中判断
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
char buffer1[100];
int i;
glbHandle = openFileRead("Files\test.txt", 1);
if (glbHandle != 0) //打开成功
{
fileGetString(buffer, elcount(buffer), glbHandle);
//write("buffer: %s", buffer);
snprintf(buffer1, elcount(buffer1), "test%c", buffer[3]);
if (strncmp("testr", buffer1, strlen(buffer1)) == 0)
{
write("有回车转移字符\r");
}
snprintf(buffer1, elcount(buffer1), "test%c", buffer[4]);
if (strncmp("testn", buffer1, strlen(buffer1)) == 0)
{
write("有换行转移字符\n");
}
}
}
打印的结果为
把fileGetString换成fileGetStringSZ
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
char buffer1[100];
int i;
glbHandle = openFileRead("Files\test.txt", 1);
if (glbHandle != 0) //打开成功
{
fileGetStringSZ(buffer, elcount(buffer), glbHandle);
//write("buffer: %s", buffer);
snprintf(buffer1, elcount(buffer1), "test%c", buffer[3]);
if (strncmp("testr", buffer1, strlen(buffer1)) == 0)
{
write("有回车转移字符\r");
}
snprintf(buffer1, elcount(buffer1), "test%c", buffer[4]);
if (strncmp("testn", buffer1, strlen(buffer1)) == 0)
{
write("有换行转移字符\n");
}
write("length: %d", strlen(buffer));
}
}
打印的结果为
这就很奇怪了,fileGetString读取的内容是5个字符,fileGetStringSZ只有4个字符,为什么还有第5个字符”n”呢,Helper文档里介绍这个函数时,明明规定了它不会读取换行符的呀
其实此换行符非彼换行符,还记得之前在介绍数组时,说过字符串也是数组,但是字符串长度要比实际的字符大1,因为每个字符串后面默认有一个结尾符,就是换行符,所以这里的换行符是存入时自带的,而不是读取的text文件里的
为什么这两个函数只能读取一行内容呢
既然调用一次这两个函数只能读取到行尾的内容,那我如何读取其余行呢
“
可以通过多次调用这两个函数,把每一行都读取出来
”
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
char buffer1[100];
int i;
glbHandle = openFileRead("Files\test.txt", 1);
if (glbHandle != 0) //打开成功
{
fileGetString(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
fileGetString(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
fileGetString(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
}
}
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
char buffer1[100];
int i;
glbHandle = openFileRead("Files\test.txt", 1);
if (glbHandle != 0) //打开成功
{
fileGetStringSZ(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
fileGetStringSZ(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
fileGetStringSZ(buffer, elcount(buffer), glbHandle);
write("buffer: %s", buffer);
}
}
为什么打印出来的结果有差异,就是由于fileGetString读取的内容里有换行符,打印到write窗口时,就换行了
关闭文件
完成文件的读取以后,必须要把这个文件关闭,释放资源,只需要调用fileClose,传入文件句柄作为参数即可
fileClose (glbHandle);
写入文件
写入文件同样遵循三步走的方式
打开文件
先以write access的方式调用函数openFileWrite打开文件
传入的参数除了文件路径外,mode的值相比读取文件时要有所不同
参数mode为0,表示文件是以text模式打开的
参数mode为1,表示文件是以binary模式打开的
参数mode为2,不仅表示文件是以text模式打开,而且写入的数据附在文件内容末尾写入,而不是覆盖
参数mode为3,不仅表示文件是以binary模式打开,而且写入的数据附在文件内容末尾写入,而不是覆盖
写入文件
写入文件调用的函数是filePutString
这里不多废话,我们用一个例子写入一些内容到刚才的test.txt文本中
on key 'a'
{
dword glbHandle = 0;
char buffer[5] = "test";
int i;
glbHandle = openFileWrite("Files\test.txt", 0);
if (glbHandle != 0) //打开成功
{
filePutString(buffer, elcount(buffer), glbHandle);
}
fileClose(glbHandle);
}
这里要注意,只有在脚本里关闭文件后,在电脑上打开test.txt才能看到写入的内容,不然的话一片空白
可以发现之前的内容全部被覆盖了
如果把mode改为2传入呢
on key 'a'
{
dword glbHandle = 0;
char buffer[5] = "test";
int i;
glbHandle = openFileWrite("Files\test.txt", 2);
if (glbHandle != 0) //打开成功
{
filePutString(buffer, elcount(buffer), glbHandle);
}
fileClose(glbHandle);
}
打开test.txt文本查看
“
如果想换一行写入呢,只需要在写入的内容前加”n”
”
关闭文件
同样地,调用函数
fileClose (glbHandle);
bin文件
bin文件是以二进制格式存入数据的文件,如果以text或notepad的形式打开,显示乱码
首先在电脑上创建一个bin文件test.bin,然后存入1、2、3三个数,这里我参考的以前的一个python脚本
def WriteBinFile():
binfilepath = r"C:UsersxxxDesktoptest.bin"
binfile = open(binfilepath, "ab+")
data = [0x01, 0x02, 0x03]
for i in data:
content = i.to_bytes(1, "big")
binfile.write(content)
if __name__ == "__main__":
WriteBinFile()
读取文件
打开文件
打开bin文件所用的函数,和打开txt文件用的函数是一样的,只是传入的参数mode值不同,必须为1,OpenFileRead
读取文件
读取bin文件的函数,和txt就不同了
可以看出,读取的内容存入参数buff中,buff是一个byte数组,也就是说,bin文件读取的内容是一个一个的byte
关闭文件
fileClose (glbHandle);
附上完整的脚本
on key 'a'
{
dword glbHandle = 0;
byte buffer[5];
int i;
int lens;
glbHandle = openFileRead("Files\test.bin", 1);
if (glbHandle != 0) //打开成功
{
lens = fileGetBinaryBlock(buffer, elcount(buffer), glbHandle);
for (i = 0; i < lens; i++)
{
write("buffer: 0x%02x", buffer[i]);
}
}
fileClose(glbHandle);
}
打印的结果为
写入文件
这部分内容不打算详细说明,只需要注意几个函数
打开bin文件的函数用的是OpenFileWrite,注意参数mode值必须为1或3
写入的函数fileWriteBinaryBlock
关闭文件的函数还是fileClose
ini文件
什么是ini文件
“
.ini文件是Initialization File的缩写,即初始化文件,是windows的系统配置文件所采用的存储格式
”
很多项目中都会把一些配置信息放入ini文件中,方便管理
而capl也提供了相关的函数读取或写入ini文件内容
首先我们先看一下ini文件内容是什么样的
ini文件由节、键、值组成
节
[Section]
参数
(键=值)
name=value
所以不管是python,还是capl,读写ini文件,都是先找节,然后在节的下面根据键找到值
读写ini文件并没有txt或bin文件那样的三步走策略,直接调用读或写的函数,传入参数就可以
读取文件
读取ini文件可以使用如下函数
我们试一下其中的两个函数:getProfileString和getProfileInt
getProfileString读取ini文件中键对应的值,以字符串的形式保存在字符串数组中,所以读取的数据不管是字母还是数字,都可以读取,读取后都是以字符串的形式保存
“
这里有个参数def要注意,这里传入一个默认值字符串,当ini文件或键没找到,就把这个默认的字符串放入buffer中保存
”
代码如下
on key 'a'
{
char absPath[256];
char buffer[100];
int lens;
getAbsFilePath("Files\test.ini", absPath, 256);
getProfileString("section1", "name", "default", buffer, elcount(buffer), absPath);
write("length: %d", strlen(buffer));
write("buffer: %s", buffer);
}
打印的结果为
可以看出,读取ini文件的键值,并不会包含任何的转义字符,这点和txt还是不同的
getProfileInt读取的值是以int类型保存到返回值里,所以读取的内容必须是数字,不能是字母或符号等
代码如下
on key 'a'
{
char absPath[256];
int speed;
getAbsFilePath("Files\test.ini", absPath, 256);
speed = getProfileInt("section2", "speed", 0, absPath);
write("speed: %d", speed);
}
打印的结果为
写入文件
写入文件使用如下函数
“
这里有个问题,我传入的文件路径不能为绝对路径,如果是绝对路径就无法写入,相对路径才可以写入,很奇怪,而读取ini文件内容时,绝对路径和相对路径都可以
”
on key 'a'
{
char absPath[256];
char buffer[5] = "test";
getAbsFilePath("Files\test.ini", absPath, 256);
writeProfileString("section1", "name", "test", "Files\test.ini");//必须用相对路径写入
write("end");
}
看一下写入的结果
如果章节或者键不存在呢,能写入吗,也就是说能创建吗
发现是可以的!!!
csv文件
什么csv文件
“
Comma-Separated Values,逗号分隔值,有时也称为字符分隔值,因为分隔字符也可以不是逗号,CSV文件由任意数目的记录组成,记录间以某种换行符分隔
”
csv文件可以用excel、text、notepad等多种方式打开
既然csv文件能够以text方式打开,那么就可以用读写txt文件的方法来读写csv文件
读取文件的代码
on key 'a'
{
dword glbHandle = 0;
char buffer[100];
char buffer1[100];
int lens;
glbHandle = openFileRead("Files\test.csv", 1);
if (glbHandle != 0) //打开成功
{
fileGetStringSZ(buffer, elcount(buffer), glbHandle);
write("length: %d", strlen(buffer));
write("buffer: %s", buffer);
snprintf(buffer1, elcount(buffer1), "test%c", buffer[strlen(buffer)-1]);
if (strncmp("testr", buffer1, strlen(buffer1)) == 0)
{
write("有回车转移字符\r");
}
}
}
打印的结果为
可以看出,和txt文件的读取方式一摸一样
写入方式在这里就不多细聊了,这里只提一点,如果想在excel方式打开的内容里,在下一格写入内容,只需要在写入的内容前面加一个逗号分割符
“
excel文件是无法在capl中直接读写的,所以很多时候会把excel文件转换成csv文件,然后用capl读取内容
”
以上就是这几种文件读写的简单介绍,请点赞,请关注,请转发,谢谢!!!
原文始发于微信公众号(汽车网络诊断通信):CAPL可以读写的几种文件