Loading...
墨滴

jasonj333

2021/11/09  阅读:37  主题:红绯

CAPL可以读写的几种文件

在CAPL学习之路的系列文章中,提及过capl读写文件的相关函数,今天介绍几种capl可读写的文件类型,用到的函数接口如下

众所周知,要把大象装冰箱,总共分三步:把冰箱门打开,把大象装进去,把冰箱门关上(这里最好用东北话,更有画面感)

串台了串台了,重来,想要读写文件,总共也分三步:打开文件,读写内容,关闭文件

根据这种思路,我们一起试着读/写文件内容

txt文件

读取文件

首先在电脑上创建一个test.txt文件,输入一些内容后保存

接下来根据三步走的思路,来实现读取文件内容

打开文件

用openFileRead函数打开文件,它是以read access的方式打开文件的

传入的参数有两个是必需的,filename是文件的路径,mode是以何种方式打开

文件路径可以是绝对路径,也可以是相对路径,绝对路径没什么可说的,相对路径就是必须把文件放入CANoe工程文件的目录下

如果是和CANoe工程同级目录,就可以直接使用test.txt文件名,如果放在次级目录下,比如放在CANoe工程目录的Files文件夹里,传入的filename就是"Files\test.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系统来说,采用回车加换行的方式来实现换行,也就是"\r\n",所以fileGetString读取的一行中包括"\r\n",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("test\r", buffer1, strlen(buffer1)) == 0)
    {
      write("有回车转移字符\\r");
    }
    
    snprintf(buffer1, elcount(buffer1), "test%c", buffer[4]);
    if (strncmp("test\n", 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("test\r", buffer1, strlen(buffer1)) == 0)
    {
      write("有回车转移字符\\r");
    }
    
    snprintf(buffer1, elcount(buffer1), "test%c", buffer[4]);
    if (strncmp("test\n", 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:\Users\xxx\Desktop\test.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等多种方式打开

excel方式打开
excel方式打开
text方式打开
text方式打开

既然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("test\r", buffer1, strlen(buffer1)) == 0)
    {
      write("有回车转移字符\\r");
    }
  }
}   

打印的结果为

可以看出,和txt文件的读取方式一摸一样

写入方式在这里就不多细聊了,这里只提一点,如果想在excel方式打开的内容里,在下一格写入内容,只需要在写入的内容前面加一个逗号分割符

excel文件是无法在capl中直接读写的,所以很多时候会把excel文件转换成csv文件,然后用capl读取内容


以上就是这几种文件读写的简单介绍,请点赞,请关注,请转发,谢谢!!!

jasonj333

2021/11/09  阅读:37  主题:红绯

作者介绍

jasonj333