LR中的编码问题
【转载】LoadRunner字符集与检查点的探讨
很多人在loadrunner测试脚本中加入中文检查点的时候会出现检查失败的情况,究竟是为什么呢?其实是被测试系统与loadrunner字符集之间的转换出现了问题。下面我们来一一解释。我们知道loadrunner在录制选项中有一个字符集的设置:Recording Options>>Advanced>>Support charset,在这里可以设置loadrunner支持的字符集。那么被测试系统的字符集与loadrunner字符集会有什么样的关系呢?下面我们以百度、Google为例子加以说明。
首先分别查看百度、Google网站的字符集是什么?打开相应的网站,通过右键查看源文件来获取它们的字符集。百度的字符集为?:gb2312,源文件代码中内容为:<!doctype html><html><head><meta http-equiv="Content-Type" content="text/html;charset=gb2312">;Google的字符集为:UTF-8,源文件代码中内容为:<!doctype html><html><head><meta http-equiv="content-type" content="text/html; charset=UTF-8">。
1、在录制百度的测试脚本的时候不设置loadrunner字符集,脚本如下:
1 Action() 2 { 3 web_reg_find("Search=body", 4 "SaveCount=times", 5 "Text=百度", 6 LAST); 7 9 web_url("www.baidu.com", 10 "URL=http://www.baidu.com/", 11 "Resource=0", 12 "RecContentType=text/html", 13 "Referer=", 14 "Snapshot=t1.inf", 15 "Mode=HTML", 16 EXTRARES, 17 "Url=/img/i2.png", ENDITEM, 18 "Url=/img/arr.gif", ENDITEM, 19 "Url=/js/bdsug.js?v=1.0.3.0", ENDITEM, 20 "Url=/cache/hps/js/hps-1.0.js", ENDITEM, 21 LAST); 22 23 if(atoi(lr_eval_string ("{times}"))>0) 24 lr_output_message("search successful"); 25 else 26 lr_error_message("search failed"); 27 28 return 0; 29 }
运行测试脚本,检查点可以通过。
2、设置loadrunner的字符集为:UTF-8,再次录制一个百度的测试脚本,测试脚本同上。这时运行测试脚本,会发现检查点没有通过,日志信息显示:Action.c(26): Error: search failed。
为什么没有设置loadrunner的字符集的检查点可以通过,而设置loadrunner的字符集为:UTF-8的检查点却不能通过呢?我们来看下这两个脚本所产生的代码生成日志信息,在测试脚本的data目录下的CodeGenerationLog.txt文件中。这时我们可以在两个文件中搜索检查点“百度”关键字,可以发现在没有设置loadrunner字符集的这个文件中可以搜索到“百度”关键字而在设置loadrunner的字符集为:UTF-8的这个文件中却搜索不到“百度”关键字。这是因为设置loadrunner的字符集为:UTF-8会把测试脚本所生成的信息会强制将gb2312转换为UTF-8。所以对于被测试系统字符集是gb2312的网站,loadrunner中又将字符集设置为:UTF-8,中文关键字作为检查点的时候可能失败。
再以google为例:
1、在录制google的测试脚本的时候不设置loadrunner字符集,脚本如下:
1 Action() 2 { 3 web_reg_find("Search=body", 4 "SaveCount=times", 5 "Text=地图", 6 LAST); 7 8 web_url("www.google.com.hk", 9 "URL=http://www.google.com.hk/", 10 "Resource=0", 11 "RecContentType=text/html", 12 "Referer=", 13 "Snapshot=t2.inf", 14 "Mode=HTML", 15 EXTRARES, 16 "Url=/extern_js/f/CgV6aC1DThICaGsrMEU4ACwrMFo4ACwrMA44ACwrMBc4ACwrMDw4ACwrMFE4ACwrMAo4AUACLCswFjgALCswGTgAmgICc2gsKzAlOAAsKzBAOAAsKzBNOAAsKzBOOAAsKzBUOAAsKzBpOAAsKzAYOAAsKzAmOAAsgAJJkAJC/-1wqCmgtU-M.js", ENDITEM, 17 "Url=/images/srpr/nav_logo73.png", ENDITEM, 18 "Url=/csi?v=3&s=webhp&action=&e=17259,17315,23628,28505,28936,29561,29810,30348,30760,31090,31127,31267,52921&ei=RTkHTtGwDYHEvgPK1firDQ&expi=17259,17315,23628,28505,28936,29561,29810,30348,30760,31090,31127,31267,52921&imc=1&imn=1&imp=1&rt=xjsls.47,prt.62,xjses.3922,xjsee.3953,ol.4000,iml.984,xjs.4109", ENDITEM, 19 "Url=/complete/search?hl=zh-CN&client=hp&xhr=t&q=n&cp=1", ENDITEM, 20 "Url=/complete/search?hl=zh-CN&client=hp&xhr=t&q=nb&cp=2", ENDITEM, 21 LAST); 22 23 if(atoi(lr_eval_string ("{times}"))>0) 24 lr_output_message("search successful"); 25 else 26 lr_error_message("search failed"); 27 28 return 0; 29 }
运行测试脚本,检查点没有通过,日志信息显示:Action.c(31): Error: search failed。
2、设置loadrunner的字符集为:UTF-8,再次录制一个google的测试脚本,测试脚本同上。这时运行测试脚本,会发现检查点通过。
为什么没有设置loadrunner的字符集的检查点不能通过,而设置loadrunner的字符集为:UTF-8的检查点却能够通过呢?我们来看下这两个脚本所产生的代码生成日志信息,在测试脚本的data目录下的CodeGenerationLog.txt文件中。这时我们可以在两个文件中搜索检查点“地图”关键字,可以发现在没有设置loadrunner字符集的这个文件中搜索不到“地图”关键字而在设置loadrunner的字符集为:UTF-8的这个文件中却能够搜索到“地图”关键字。这是因为没有设置loadrunner的字符集为:UTF-8,不会自动把测试脚本所生成的信息转换为UTF-8而是转换为其他的Encode。所以对于被测试系统字符集是UTF-8的网站,loadrunner中必须将字符集设置为:UTF-8,中文关键字作为检查点的时候才能通过。
附:录制脚本后,切换到树视图中,打开相应的脚本页面。在右侧的PageView中录制的脚本呈现中文版式,但是当切换到Server Response中,所有的中文全部换成的乱码,如“勌缞仫訆”,原因是服务器端没有把响应的编码设置为gb2312,可以找到Web.Config文件,在<system.web>….</system.web>节点加入<globalization requestEncoding="gb2312" responseEncoding="gb2312" fileEncoding="gb2312"/>后再次录制脚本,乱码变中文。(gb2312:中国大陆制订了简体汉字的字符集)
--------------------------------------------------------------------------------分割线-------------------------------------------------------------------------------------
以上为网上装载的文章,因为平时在性能测试脚本调试时经常遇到有关LR的编码的问题,所以对于经常忽略的编码问题引起了重视。首先对于以上文章,有几点可能不是很准确,在我的机器上(WIN7+IE8)上面,百度和谷歌网站编码都是UTF-8的。而且发现大部分的网站都是UTF-8编码,平时我在录制这些网站时,loadrunner中的字符集也设置为了UTF-8,录制完后LR的tree视图中的HTML视图中中文都被显示为乱码,生成和回放日志中的中文也为乱码,中文检查点会失败。(偶有发现有些机器也是WIN+IE8(详细配置是否一致未知),进行录制这些网站脚本时,HTML视图中也显示为乱码,但是生成和回放日志中的中文不是乱码而显示正常,并且直接使用中文检查点也可以成功)
中文检查点问题
对于中文检查点(或是关联边界)失败的问题是有其解决办法的,即在检查中文时,先使用lr_convert_string_encoding函数将中文从本地编码转换成UTF-8编码。再作为检查点,另外注意lr_convert_string_encoding将原字符串转换为UTF-8后会在字符串的最后面加\x00,这个需要使用lr_save_string(lr_eval_string("{str_param}"),"str_param")来处理或使用C的字符串截断函数strtok来截取掉。
总结为:当发现LR的tree视图中的HTML视图中你所看到的是什么样的,那么脚本中检查点检查的内容就要写成什么样的,比如需要检查的内容是:涓佷紵,那么脚本中检差点函数中检查的内容写:涓佷紵就可以检查通过,当然我们肯定不知道这样的乱码究竟对应的中文是什么,一般情况下,这种乱码的编码格式是UTF-8的,如果要看到的它的真实面貌就需要将“涓佷紵”通过lr_convert_string_encoding从UTF-8编码转换成本地编码(一般为gb2312)再看。比如该例为:丁伟,所以反过来,我们在添加检查点时应该是先将中文丁伟通过lr_convert_string_encoding从本地编码转换成UTF-8编码,然后作为检查点的内容,关联的边界也是同样的道理。例子如下:
1 //将中文从本地编码转到UTF8编码 2 lr_convert_string_encoding( lr_eval_string("{PPMname}"), 3 LR_ENC_SYSTEM_LOCALE, 4 LR_ENC_UTF8, 5 "para_tstring" ); 6 //lr_output_message("Notify: para_tstring:%s",lr_eval_string("{para_tstring}")); 7 8 //置于变量中 9 strcpy( para_string, 10 lr_eval_string("{para_tstring}") ); 11 12 //去掉末尾的\x00 13 para_str = (char *)strtok(para_string,"\\"); 14 //lr_output_message("Notify:para_str:%s",para_str); 15 16 //置于参数中 17 lr_save_string( para_str, 18 "para_str" ); 19 20 //检查点 21 web_reg_find("Fail=NotFound", 22 "Search=Body", 23 "SaveCount=countxx", 24 //"Text=2012 涓蒋iNOC PDU", 25 "Text={para_str}", 26 LAST); 27 28 //用作右边界 PPMid 0cf6478f-cc83-45a4-9f28-e7396b72d66e 29 web_reg_save_param_ex( 30 "ParamName=correlation_PPMid", 31 "LB=option value=\\\'", 32 "RB=\\\' title=\\\'{para_str}", 33 "Ordinal=1", 34 SEARCH_FILTERS, 35 "Scope=BODY", 36 LAST);
URL和HTML编码:
关于编码问题,在LR的脚本中可能还会有这样的一个问题,就是有时发现录制的脚本中会有很多含有很多%的ASCII码串,比如下面这个函数:
1 web_custom_request("ServiceCall_3", 2 "URL=http://w3scm-uata.huawei.com/scm/scmui/issui/webengine/huawei/wpf/frame/base/ServiceCall", 3 "Method=POST", 4 "Resource=0", 5 "RecContentType=text/html", 6 "Referer=http://w3scm-uata.huawei.com/scm/scmui/issui/webengine/huawei/wpf/Framework#/!0/!C000000000028588/!./!./!./!./!./!1384247116426", 7 "Snapshot=t113.inf", 8 "Mode=HTTP", 9 "Body=name=iss.sch.mrmatch.SeachDashbordDetail&applicationID=&sub_app_id=¶ms=" 10 "%7B%22wpf_dup_token%22%3A%22-19613772761384247117836%22%2C%22sessionData%22%3A%22%7B%5C%22tag_data%5C%22%3A%7B%5C%22currentModel%5C%22%3A
略
34 LAST);
这种含有%的串又是什么呢?如果要对这种串进行参数化该怎么实现?
发现这种串为URL编码格式,因为URL只能使用ASCII码,所以有很多特殊字符都需要转码为URL的编码,关于URL编码对照表可以参考:HTML URL 编码
这种编码可能有两类,一类是诸如双引号”这种特殊字符被转为了%串,如:%22;另一类就是有的中文被转成了这种格式。
URL编码 URL只能使用ASCII字符集来通过因特网进行发送。由于URL常常会包含ASCII集合之外的字符,URL必须转换为有效的ASCII格式。URL编码使用"%"其后跟随两位的十六进制数来替换非ASCII字符。常见例子:
1)+ 表示空格(在 URL 中不能使用空格) %20
2)/ 分隔目录和子目录 %2F
3)? 分隔实际的 URL 和参数 %3F
4)% 指定特殊字符 %25
5)# 表示书签 %23
6)& URL中指定的参数间的分隔符 %26
HTML特殊符号编码 由于HTML的语法特点,有些字符需要使用特殊的符号串来表示,比如HTML本身语法中就包含有<、>等,所以当要显示这两个字符时就要使用特殊的符号串。常见例子:
HTML 原代码 显示结果 描述
< < 小于号或显示标记
> > 大于号或显示标记
& & 可用于显示其它特殊字符
" “ 引号
® ® 已注册
© © 版权
™ ™ 商标
  半个空白位
  一个空白位
不断行的空白
<sub></sub> H2O 下标标签
<sup></sup> X2 上标标签
注意:URL或HTML的转码都发生在字符编码例如UTF-8等之上的,即在发送时是先经过URL或HTML的转码再经过字符编码后再发送的,而在解码时是先进行字符编码对应的解码再进行URL或HTML的反解码的。所以对于脚本中这种串的参数化问题,可以使用LR提供的web_convert_param函数来进行编码转换来实现,例如中文:丁伟, 录制的脚本中为:%E4%B8%81%E4%BC%9F,那么参数化就应该是将中文丁伟转成%E4%B8%81%E4%BC%9F,一般为先把中文丁伟转成UTF-8编码,再转成URL格式就刚好对应上了。例如:
lr_save_string("丁伟","para_tstring"); ---(注意:实际使用时会添加参数para_tstring,并设置多个值,这里只是为简化例子) lr_output_message("Notify:para_tstring:%s",lr_eval_string("{para_tstring}"));
lr_convert_string_encoding(lr_eval_string("{para_tstring}"),LR_ENC_SYSTEM_LOCALE,LR_ENC_UTF8,"para_tstring"); lr_output_message("Notify:LOCALE-->UTF8 para_tstring:%s",lr_eval_string("{para_tstring}")); web_convert_param("para_tstring", "SourceEncoding=PLAIN", "TargetEncoding=URL", LAST ); lr_output_message("Notify:PLAIN-->URL para_tstring:%s",lr_eval_string("{para_tstring}")); Starting iteration 1. Starting action Action. Action.c(5): Notify:para_tstring:丁伟 Action.c(7): Notify:LOCALE-->UTF8 para_tstring:涓佷紵 Action.c(9): web_convert_param was successful [MsgId: MMSG-26392] Action.c(11): Notify:PLAIN-->URL para_tstring:%E4%B8%81%E4%BC%9F%00 Ending action Action. Ending iteration 1.
函数web_convert_param详见在线帮助文档,直观/感性认识:
HTML URL Plain Text
<mytag>& %3Cmytag%3E%26 <mytag>&
以下FAQ可供参考:
Q1、录制的脚本乱码?
LR录制的脚本中可能会有乱码,主要是当URL中有中文时。
通过如下问题可以解决此问题:
a) Go to Vugen -> Tools -> Recording Options -> Advanced
b) Check the option that reads "Support Charset" and select "UTF-8"
Q2、回放乱码?
有一类乱码问题是:IE访问页面一切正常,但是LR回放时在run viewer中显示的页面为乱码。这一问题一般是由于页面保存时的编码格式和页面中的charset格式不一致引起的(html头中通常会有<meta. http-equiv="Content-Type" c>)。遇到这类问题,只需要将页面做另存为,将保存的编码格式和页面中的charset格式统一起来就可以了。引起问题的原因是:IE浏览器解码时会优先考虑文件的保存编码格式,而后考虑页面中的charset格式,(正常情况下两者是一致的),而run viewer是直接使用页面中的charset格式打开的。
例如:charset=gb2312,但是文件的保存的编码格式是UTF-8,IE访问时会以UTF-8解码,而run viewer却是以GB2312格式解码,以GB2312解UTF-8自然是乱码。出现乱码的原因和解决办法很可能不全面,请大家补充。说到底,还是run viewer功能比较简单引起的。run viewer中的乱码不影响测试结果。
Q3、LR中乱码相关设置和用法?
1)vugen的Tools -> Recoding Options -> Advanced -> Support charset -> UTF-8
2)Vuser-->运行时设置-->浏览器-->浏览器仿真-->更改-->使用浏览器-->语言下来选择 “中文(中国)”
3)在LoadRunner中,为我们提供了一个字符串编码转换的函数lr_convert_string_encoding,用法如下:
int lr_convert_string_encoding ( const char *sourceString, const char *fromEncoding, const char *toEncoding, const char *paramName);”
例子:
lr_convert_string_encoding(lr_eval_string("{NewParam_1}"),LR_ENC_SYSTEM_LOCALE,LR_ENC_UTF8,"str");
strcpy(tmp,lr_eval_string("{str}"));
4)修改代码,增加请求头字段:
web_add_header("Content-Type", "text/xml; charset=UTF-8");
------------------------------------------------------------------------------------分割线------------------------------------------------------------------------------
以上关于LR中乱码问题的解决方法基本可以解决大部分的转码问题了,但在某些情况下可能需要将发送的数据中某些符号转换成其他的形式,比如将"+"号转换成"%2B"等等,这里提供一个通用的C语言函数来处理该类问题,该函数可以把一个原始字符串中的某个字符串(单个或多个字符)替换为指定的字符串(单个或多个字符)。代码如下:
char *ReplaceStr(char *sSrc,char *sMatchStr,char *sReplaceStr) { int StringLen; char caNewString[1024]; char *FindPos; FindPos =(char *)strstr(sSrc, sMatchStr); if( (!FindPos) || (!sMatchStr) ) { return sSrc; } while( FindPos ) { memset(caNewString, 0, sizeof(caNewString)); StringLen = FindPos - sSrc; strncpy(caNewString, sSrc, StringLen); strcat(caNewString, sReplaceStr); strcat(caNewString, FindPos + strlen(sMatchStr)); strcpy(sSrc, caNewString); FindPos =(char *)strstr(sSrc, sMatchStr); } free(FindPos); return sSrc; }
使用方法:
在脚本的“globals.h”中或Action()函数之上,复制上面代码,然后在Action()函数脚本中,用这个函数替换其中的特殊符号,例如:
char *str = NULL; str = ReplaceStr(lr_eval_string("{para}"),"+","%2B"); lr_output_message("Notify:str:%s",str);
上面调用ReplaceStr的代码的意思是,把关联函数获取的值{para}参数中的“+”号替换为“%2B”,并放入变量str 中,后面使用变量str即可。有多个替换的把函数调用多遍。需要注意
注意:上面函数中定义的局部变量caNewString数组大小,但传入的原字符串sSrc很大时,caNewString的空间可能不足,此时就应该将1024换成更大的值。
---------------------------------------------附:
关于字符编码:字符集和字符编码(Charset & Encoding)