摘要:这是一篇写给新手的关于CODESYS开发环境的小白教程,一看就懂......
在以前的《CODESYS开发教程7-字符串及其基本操作》教程中,介绍了字符串及其基本操作,有朋友看了以后觉得不过瘾,希望有一些关于字符串的更加深入的介绍。今天的教程重点给大家讲解一下长字符串操作的库及相关函数,还有长字符串(长度>255)处理相关的一些骚操作,比如自己写的字符串长度函数、字符串中查找字符的函数等。
一、关于STRING类型的说明
CODESYS的STRING字符串类型有几点重要的规则需要给大家介绍一下。
1.字符串存储
字符串实际上是一个字符数组。比如定义一个字符串并赋值。
str : STRING:=’CODESYS’;
str实际上是一个长度为7的字符数组,可以使用数组的方式来访问。也就是说,可以通过str[0]来获得字符串的首字符’C’。在实际存储中,字符是采用ASCII码的形式来保存的。在判断str[0]是否为字符C时,需要跟字符C的ASCII码来进行比较,即str[0]=16#43 或 str[0]=67 会输出TRUE。但是使用str[0]=‘C’则会报编译错误。
在CODESYS中没有字符变量,但是可以用BYTE来定义字符,比如16#4A为字母J。
2.字符串大小
在定义字符串类型的变量时,如果不指定字符串的长度,字符串变量允许的最多字符数为80。
str : STRING:=’CODESYS’; //此时str实际为最大长度80个字符的数组str[0..79]
字符串变量实际需要的内存为字符串的长度加一个字节,即上面定义的str变量所占存储空间为81个字节(SIZEOF(str)=81)。
3.长字符串
CODESYS环境没有限制字符串变量类型的长度,因此长度超过255的字符串也是合法的(C语言的字符串最大长度为255)。例如可以定义个长度为1000字节的字符串:
str1 : STRING(1000);
需要注意的是,在《字符串及其基本操作》中介绍的字符串处理函数处理的字符数不能超过255,即只能对STRING(255)类型的字符串进行操作。长度超过255字符的字符串可以通过数组读取的方式取出来单独处理。
受字符数限制的字符串处理函数主要包括:CONCAT、DELETE、FIND、INSERT、LEFT左选、LEN、MID、REPLACE、RIGHT。
测试程序如下:
结果如下图所示:
如上图中所示, 字符串类型变量str1的长度为3,所占内存空间大小是81字节。
对于长字符串strLong,其实际长度为10000。使用LEN()函数获取的长度为255,而使用函数StrLenA()获取的长度为正常的10000,所占内存空间大小为10001字节。
4.关于长字符串支持的最长度
测试发现,编译器(3.5.16)支持的长字符串最大尺寸为4294967295,即2^32-1。用仿真运行测试了一下我用的这个控制器(微秒P4CM),发现最多只能支持2147483647,即2^31-1,超过该长度编译能通过,但是下载运行时会报错,要么是控制器内存不足,要么是底层数据类型的长度限制导致的。
PROGRAM main
VARi : INT;str1 : STRING;strLong : STRING(10000);t0 : INT;t1 : UINT;t2 : INT;t3 : DINT;t4 : UINT;
END_VARstr1:='abc';
FOR i:=0 TO 9999 DOstrLong[i]:=16#3C;
END_FOR
t0:=LEN(str1);
t1:=SIZEOF(str1);
t2:=LEN(strLong);
t3:=StrLenA(ADR(strLong));
t4:=SIZEOF(strLong);
二、长字符串的处理
长字符串(长度>255)的处理有两种方式:一种是使用CODESYS提供的长字符串函数库;一种是自己编程处理。
1.使用长字符串函数库StringUtils
对于长度超过255个字符的字符串,CODESYS提供了相应的库,库名称是StringUtils,如下图所示。
主要包含ANSI和Unicode两种类型字符串的处理函数。
理论上,可以处理最大长度4294967295(2^32-1)的字符串。
函数功能 | CODESYS-ST | 备注 |
长度 | StrLenA/StrLenW | 仅统计字符数,不包含结束符 |
字符串比较 | StrCmpA/StrCmpW、StrCaseCmpA/StrCaseCmpW、StrCaseCmpEndA、StrCaseCmpStartA | 带Case的不区分大小写 |
字符串拷贝 | StrCpyA/StrCpyW | |
中间取位 | StrMidA/StrMidW | |
字符串连接 | StrConcatA/StrConcatW | |
空字符串检查 | StrIsNullOrEmptyA/StrIsNullOrEmptyW | |
删除 | StrDeleteA/StrDeleteW | |
替换 | StrReplaceA/StrReplaceW | |
查找 | StrFindA/StrFindW、StrCaseFindA/StrCaseFindW | 带Case的不区分大小写 |
移除空字符 | StrTrimA、StrTrimEndA、StrTrimStartA | |
??? | StrPadLeftA/StrPadLeftW、StrPadRightA/StrPadRightW | |
字符或字符串的大小写转换 | StrToLowerA、StrtoUpperA、CharToUpper/WCharToUpper |
//注意:以上函数的字符串类型输入均为POINTER TO BYTE,即指向字节的指针。
以下str均为STRING类型变量。
(1)CharToUpper(ch)
将字符ch转换为大写字符。
示例:ch:=CharToUpper('s'); //结果为'S'
(2)StrCmpA(ADR(str1),ADR(str2))、StrCaseCmpA()
比较字符串str1和str2。str1和str2相等时返回0,str1小于str2时返回-1,str1大于str2时返回1,str1或str2为非法字符串(如空指针)时返回-2。
这里的大于和小于开始我也不明白是什么意思,测试了一下发现是根据字符串的ASCII码来区分的。小于意思就是str1的ASCII码值小于str2的ASCII码,即’a’和‘b’比较会返回-1,而‘A’和‘a’比较会返回1。而字符串的比较就比较魔幻了,不太清楚返回结果的规则是什么,例如‘a’和‘ab’比较会返回-1,而‘bcd’和‘ab’比较会返回1。对于字符比较还有点用途,但是对于字符串比较有何用处,实在是没想出来~~☹
注意:StrCmpA比较时区分大小写,StrCaseCmpA比较时不区分大小写。
示例:bFlag:=StrCmpA(ADR(str1),ADR(str2));
(3)StrFindA(ADR(str1),ADR(str2),uiStart)、StrCaseFindA
在字符串str1中查找指定的字符串str2。找到字符串str2会返回字符串的首位置,未找到返回0,给定空字符串会返回-1。
注意:这里的str1和str2最多只能为STRING(255)。
示例:str1:=’ABCD’; str2:=’CD’;
pos:=StrFindA(ADR(str1), ADR(str2), 1); //pos=3
(4)StrConcatA(ADR(str1),ADR(str2),iBuffSize)
将字符串str1连接到str2后面,通常是将str2加到str1后面。如果iBuffSize大小不够或者给定的字符串为NULL时,将不进行连接并返回FALSE。
注意:iBuffSize的值必须比大于等于LEN(str1)+LEN(str2)+1,否则字符串连接会失败。
示例:str1:=’ABCD’; str2:=’CD’;
flag:=StrFindA(ADR(str1), ADR(str2), 7); //str2=’CDABCD’,flag=TRUE
flag:=StrFindA(ADR(str1), ADR(str2), 7); //str2=’CD’,flag=FALSE
(5)StrCpyA(pBuf,iBuffSize,ADR(str))
字符串拷贝,即从字符串str中拷贝iBuffSize个字节到缓冲区pBuf。拷贝完成后会返回拷贝的字符数,该数据包含结束符’\0’或“\0”。
注意:pBuf不能为NULL。另外iBuffSize是包含结束符的个数,即12表示实际只能拷贝11个字符。
示例:str1:=’’; str2:=’CDEFG’;
num:=StrCpyA(ADR(str1),3,ADR(str2)); //str1=’CD’
(6)StrDeleteA(ADR(str),iLen,iPos)
从字符串str中的iPos开始删除长度为iLen的字符。iPos=1为从第一个字符开始。
示例:str:=’ABCD’
StrDeleteA(ADR(str),2,1); //str=’CD’
(7)StrIsNullOrEmptyA(ADR(str))
判断字符串str是否为NULL或空。
(8)StrLenA(ADR(str))
获取字符串str长度。该函数是通过寻找字符串中的结束字符’\0’来实现的。如果str为NULL时会返回-1。
(9)StrMid(ADR(str),iBuffSize,iLen,iPos,ADR(strDest),uiResBufsize)
获取字符串str中从iPos开始长度为iLen的字符。iPos=1为从第一个字符开始。
(10)StrPadLeftA(ch,ADR(str1),ADR(str2),iBuffSize)、StrPadRightA
将字符串str1从左侧开始填充为指定字符ch,填充个数iBuffSize-LEN(str1),结果放在字符串str2中,并返回TRUE。当iBuffSize小于str1的字符个数时,不进行填充并返回FALSE。
示例:str1:=‘ab’
flag:=StrPadLeftA(16#41,ADR(str1),ADR(str2),7); //str2=’AAAAAab’
(11)StrReplaceA(ADR(str),iBuffSize,ADR(str2),iLenIn,iLenToRe,iLenToReWith,iPos)
将字符串中从iPos开始长度为iLen的字符替换为str2。iPos=1为从第一个字符开始。
注意:这里的str1和str2最多只能为STRING(255)。
示例:str1:=’ab’; str2:=’2’;
StrReplaceA(ADR(str1),7,ADR(str2),2,2,2,1); //str1=’2b’
写到这里忍不住吐槽一下CODESYS的帮助文档真的是垃圾,中间这几个长度参数根本看不懂是什么意思…☹,大家真要用的话自己去测试,我实在是受够了~~
(12)StrToLowerA、StrToUpperA
字符串中字符的大小写转换。
示例:str:=’abcd’;
StrToUpperA(ADR(str)); //str=’ABCD’
(13)StrTrimA(ADR(str))、StrTrimEndA、StrTrimStartA
移除字符串中的空白字符,空白字符指ASCII码为9、10、13、32的字符。
后面两个函数为仅移除结尾或开头的空白字符。
示例:str:=’ abcd ’;
StrTrimA(ADR(str)); //str=’abcd’
对于UNICODE字符串的相关处理函数用法基本类似,这里就不一一列举了(主要是我懒病犯了~~😊),大家可以自己去看帮助文档。
2.自己编程处理
在第一节里面有介绍STRING类型实际上字符数组,操作起来非常方便,因此可以自己编写相关的字符串处理函数。废话就不多说了,先上示例:
(1)长字符串长度函数FN_LEN
函数功能类似于StrLenA(),代码如下:
FUNCTION FN_LEN : DINT
VAR_INPUTpstr1 : POINTER TO BYTE;
END_VAR
VARi : DINT;strl : DINT;
END_VAR
strl:=0;
i:=0;
WHILE (pstr1[i]<> 16#0) DOi:=i+1;strl:=strl+1;
END_WHILE
FN_LEN:=strl;
运行结果如下图所示:
(2)长字符串查找函数FN_FIND
函数功能类似于StrFindA()函数,代码如下:
FUNCTION FN_FIND : DINT
VAR_INPUTpstr1 : POINTER TO BYTE;pstr2 : POINTER TO BYTE;uiStart : UINT;
END_VAR
VARi : DINT;strl : DINT;pos : DINT;iStart : DINT;
END_VAR
pos:=0;
IF uiStart>0 THENiStart:=uiStart;
ELSEiStart:=0;
END_IF
strl:=StrLenA(pstr1);
FOR i:=iStart TO strl-1 DOIF (pstr1[i]<> 16#0) AND (pstr1[i]=pstr2^) THENpos:=i+1;EXIT;ELSEpos:=0;END_IF
END_FOR
FN_FIND:=pos;
运行结果如下图所示:
例子就写这么多,复杂的我也不太会~~(懒病是没有救的……)。从上面的例子可以看到,各种字符串处理函数就是遍历字符串,找到特定的字符并进行处理。其实字符串操作基本都是这个套路,大家可以根据自己的需要去开发一下功能更加强大的字符串处理函数。
五、总结
对于长度超过255个字符的字符串,想偷懒的话就直接用CODESYS提供的StringUtils库,如果这个库还不能满足你的要求,那当然是自己动手了(做到这一步要恭喜你了,功力又提升了,是不是考虑自己弄个字符串函数库?😊)。大家可以看到,本文里面对于长字符串的处理都是用指针的方式在传递参数,其实在自己写的时候直接用STRING也是可以的,但是由于CODESYS的类型检查非常严格,字符串类型STRING作为函数参数时,STRING和STRING(255)是不同的,这就导致实际使用的时候不如指针方便,因为指针是没有传递字符串长度的限制问题。好吧,这么简单的事实想必大家都是知道的,终于发现我是真的很啰嗦~~
------------------
原创不易,感兴趣的多支持!