作者:朱克锋

邮箱:zhukefeng@iboxpay.com

转载请注明出处:http://blog.csdn.net/linux_zkf

TLV是一种可变格式,意思就是:

Type类型, Lenght长度,Value值;

Type和Length的长度固定,一般那是2、4个字节(这里统一采用4个字节);

Value的长度有Length指定;

解码方法:

1.      读取type 用ntohl转换为主机字节序得到类型,指针偏移+4

2.      读取lengh用ntohl转换为主机字节序得到长度;指针偏移+4

3.      根据得到的长度读取value,若value数据类型为int、char、short,用ntohl转换为主机字节序,指针偏移+4;若value数据类型为字符串类型,指针偏移+length

类型(Type)字段是关于标签和编码格式的信息;

长度 (Length)字段定义数值的长度;

内容(Value)字段表示实际的数值。

因此,一个编码值又称TLV(Type,Length,Value)三元组。编码可以是基本型或结构型,如果它表示一个简单类型的、完整的显式值,那么编码就是基本型 (primitive);如果它表示的值具有嵌套结构,那么编码就是结构型(constructed)。

以上是对tlv的简单解释,从网上搜集的资源来看,用C、C++来写的开源软件都非常的复杂,我这里使用了很少的OC代码实现了一个简单实用的TLV解析库,供各位参考使用

TLV数据结构

//

//  TLV.h

//  CashBox

//

//  Created by ZKF on 13-11-18.

//  Copyright (c) 2013年 ZKF. All rights reserved.

//

#import <Foundation/Foundation.h>

@interface TLV : NSObject

@property (nonatomic, assign) NSInteger length;

@property (nonatomic, retain) NSString *value;

@property (nonatomic, retain) NSString *tag;

@end

//

//  TLV.m

//  CashBox

//

//  Created by ZKF on 13-11-18.

//  Copyright (c) 2013年 ZKF. All rights reserved.

//

#import "TLV.h"

@implementation TLV

@synthesize tag;

@synthesize value;

@synthesize length;

@end

解析数据结构

//

//  LPositon.h

//  CashBox

//

//  Created by ZKF on 13-11-18.

//  Copyright (c) 2013年 ZKF. All rights reserved.

//

#import <Foundation/Foundation.h>

@interface LPositon : NSObject

@property (nonatomic,assign) NSInteger vl;

@property (nonatomic,assign) NSInteger position;

@end

//

//  LPositon.m

//  CashBox

//

//  Created by ZKF on 13-11-18.

//  Copyright (c) 2013年 ZKF. All rights reserved.

//

#import "LPositon.h"

@implementation LPositon

@synthesize vl;

@synthesize position;

@end

解析代码

//

//  SAXUnionFiled55Utils.h

//  CashBox

//

//  Created by ZKF on 13-11-18.

//  Copyright (c) 2013年 ZKF. All rights reserved.

//

#import <Foundation/Foundation.h>

#import "TLV.h"

#import "LPositon.h"

@interface TLVParseUtils : NSObject

-(NSArray*) saxUnionField55_2List:(NSString*) hexfiled55;

@end

//

//  SAXUnionFiled55Utils.m

//  CashBox

//

//  Created by ZKF on 13-11-18.

//  Copyright (c) 2013年 ZKF. All rights reserved.

//

#import "TLVParseUtils.h"

@implementation TLVParseUtils

/**

* 银联55域

*

* 本域将根据不同的交易种类包含不同的子域。银联处理中心仅在受理方和发卡方之间传递这些适用于IC卡交易的特有数据,而不对它们进行任何修改和处理。

* 为适应该子域需要不断变化的情况

* ,本域采用TLV(tag-length-value)的表示方式,即每个子域由tag标签(T),子域取值的长度(L)和子域取值(V)构成。

* tag标签的属性为bit

* ,由16进制表示,占1~2个字节长度。例如,"9F33"为一个占用两个字节的tag标签。而"95"为一个占用一个字节的tag标签

* 。若tag标签的第一个字节

* (注:字节排序方向为从左往右数,第一个字节即为最左边的字节。bit排序规则同理。)的后五个bit为"11111",则说明该tag占两个字节

* ,例如"9F33";否则占一个字节,例如"95"。子域长度(即L本身)的属性也为bit,占1~3个字节长度。具体编码规则如下: a)

* 当L字段最左边字节的最左bit位(即bit8)为0,表示该L字段占一个字节,它的后续7个bit位(即bit7~bit1)表示子域取值的长度,

* 采用二进制数表示子域取值长度的十进制数

* 。例如,某个域取值占3个字节,那么其子域取值长度表示为"00000011"。所以,若子域取值的长度在1~127

* 字节之间,那么该L字段本身仅占一个字节。 b)

* 当L字段最左边字节的最左bit位(即bit8)为1,表示该L字段不止占一个字节,那么它到底占几个字节由该最左字节的后续7个bit位

* (即bit7~bit1)的十进制取值表示。例如,若最左字节为10000010,表示L字段除该字节外,后面还有两个字节。其后续字节

* 的十进制取值表示子域取值的长度。例如,若L字段为"1000 0001 1111 1111",表示该子域取值占255个字节。

* 所以,若子域取值的长度在128~255字节之间,那么该L字段本身需占两个字节

*

* @return tlv NSArray

*/

-(NSArray*) saxUnionField55_2List:(NSString*) hexfiled55

{

if (nil == hexfiled55) {

}

return [[[self builderTLV:hexfiled55] retain] autorelease];

}

-(NSArray*) builderTLV:(NSString *)hexString

{

NSMutableArray *arr = [[[NSMutableArrayalloc] initWithCapacity:10] autorelease];

int position = 0;

while (position != hexString.length) {

NSString * _hexTag = [self getUnionTag:hexString P:position];

NSLog(@"hex tag :%@",_hexTag);

if ([_hexTag isEqualToString:@"00"] || [_hexTag isEqualToString:@"0000"]) {

position += _hexTag.length;

continue;

}

position += _hexTag.length;

LPositon *l_position = [[[self getUnionLAndPosition:hexString P:position] retain] autorelease];;

int _vl = l_position.vl;

NSLog(@"value len :%i",_vl);

position = l_position.position;

NSString* _value = [hexString substringWithRange:NSMakeRange(position, _vl * 2)];

NSLog(@"value :%@",_value);

position = position + _value.length;

TLV *tlv = [[[TLV alloc] init] autorelease];

tlv.tag = _hexTag;

tlv.value = _value;

tlv.length = _vl;

[arr addObject:tlv];

}

return arr;

}

int ChangeNum(char * str,int length)

{

char  revstr[128] = {0};  //根据十六进制字符串的长度,这里注意数组不要越界

int   num[16] = {0};

int   count = 1;

int   result = 0;

strcpy(revstr,str);

for (int i = length - 1;i >= 0;i--)

{

if ((revstr[i] >= '0') && (revstr[i] <= '9')) {

num[i] = revstr[i] - 48;//字符0的ASCII值为48

} else if ((revstr[i] >= 'a') && (revstr[i] <= 'f')) {

num[i] = revstr[i] - 'a' + 10;

} else if ((revstr[i] >= 'A') && (revstr[i] <= 'F')) {

num[i] = revstr[i] - 'A' + 10;

} else {

num[i] = 0;

}

result = result+num[i]*count;

count = count*16;//十六进制(如果是八进制就在这里乘以8)

}

return result;

}

-(LPositon *)getUnionLAndPosition:(NSString *)hexString P:(NSInteger) position

{

NSString *firstByteString = [hexString substringWithRange:NSMakeRange(position, 2)];

int i = ChangeNum((char *)[firstByteString UTF8String],2);

NSString * hexLength = @"";

if (((i >> 7) & 1) == 0) {

hexLength = [hexString substringWithRange:NSMakeRange(position, 2)];

position = position + 2;

} else {

// 当最左侧的bit位为1的时候,取得后7bit的值,

int _L_Len = i & 127;

position = position + 2;

hexLength = [hexString substringWithRange:NSMakeRange(position, _L_Len * 2)];

// position表示第一个字节,后面的表示有多少个字节来表示后面的Value值

position = position + _L_Len * 2;

}

LPositon *LP = [[[LPositon alloc] init] autorelease];

LP.vl = ChangeNum((char *)[hexLength UTF8String],2);

LP.position = position;

return LP;

}

-(NSString*) getUnionTag:(NSString* )hexString P:(NSInteger) position

{

NSString* firstByte = [hexString substringWithRange:NSMakeRange(position, 2)];

int i = ChangeNum((char *)[firstByte UTF8String],2);

if ((i & 0x1f) == 0x1f) {

return [hexString substringWithRange:NSMakeRange(position, 4)];

} else {

return [hexString substringWithRange:NSMakeRange(position, 2)];

}

}

@end

测试代码

- (void)viewDidLoad

{

[superviewDidLoad];

// Do any additional setup after loading the view, typically from a nib.

TLVParseUtils*s = [[[TLVParseUtilsalloc] init] autorelease];

NSArray *tlvArr =  [s saxUnionField55_2List:@"9F260879CC8EC5A09FB9479F2701809F100807010199A0B806019F3704000000009F360201C2950500001800009A031205089C01609F02060000000000005F2A02015682027D009F1A0201569F03060000000000009F3303E0F0F09F34036003029F3501119F1E0832303033313233318405FFFFFFFFFF9F090220069F4104000000019F74064543433030319F631030313032303030308030303030303030"];

NSLog(@"tlv arr :%@",tlvArr);

}

获取源代码请到https://github.com/zhukefeng-ios/TVLParse

OC封装的TLV数据格式解析库的更多相关文章

  1. Pugixml一种快速解析XML文件的开源解析库

    Pugixml是一个轻量级的C++ XML开源解析库,DOM形式的解析器.接口和丰富的遍历和修改操作,快速的解析,此外支持XPath1.0实现数据查询,支持unicode编码: 使用Pugixml可通 ...

  2. Tomjson - 一个"短小精悍"的 json 解析库

    Tomjson,一个"短小精悍"的 json 解析库,tomjson使用Java语言编写,主要作用是把Java对象(JavaBean)序列化为json格式字符串,将json格式字符 ...

  3. 【转】编译quickfast解析库(沪深level2行情转码库)

     转自http://blog.csdn.net/hacode/article/details/7065889 编译quickfast解析库(沪深level2行情转码库) 目录(?)[-] 1 下载源代 ...

  4. iOS开源JSON解析库MJExtension

    iOS中JSON与NSObject互转有两种方式:1.iOS自带类NSJSONSerialization 2.第三方开源库SBJSON.JSONKit.MJExtension.项目中一直用MJExte ...

  5. IOS学习:常用第三方库(GDataXMLNode:xml解析库)

    IOS学习:常用第三方库(GDataXMLNode:xml解析库) 解析 XML 通常有两种方式,DOM 和 SAX: DOM解析XML时,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过 ...

  6. Tomjson - json 解析库

    Tomjson - 一个"短小精悍"的 json 解析库 Tomjson,一个"短小精悍"的 json 解析库,tomjson使用Java语言编写,主要作用是把 ...

  7. go通过swig封装、调用c++共享库的技术总结

    go通过swig封装.调用c++共享库的技术总结 @(知识记录) 1 简介 最近在研究golang,希望能对目前既有的python服务做一些优化,这些服务目前已经占用了6-7台机器.选择golang的 ...

  8. HTML解析库Gumbo简单使用记录

    目录 Gumbo简介 使用记录 1.GumboNode的类型 2.简单的使用 Gumbo简介 Gumbo是谷歌开源的一个纯C编写的HTML解析库,性能很好,就是用起来比较麻烦. github地址htt ...

  9. GDataXMLNode:xml解析库

    IOS学习:常用第三方库(GDataXMLNode:xml解析库) 解析 XML 通常有两种方式,DOM 和 SAX: DOM解析XML时,读入整个XML文档并构建一个驻留内存的树结构(节点树),通过 ...

随机推荐

  1. 2D动态光照

    对场景内所有点发出射线, 如果射线被某条边阻挡, 则射线停留在阻挡的边上, 如果射线顺利抵达终点, 则对射线偏移-0.001, +0.001角度, 再射出2条射线, 停留在后续的阻挡边上. 把最终的射 ...

  2. Fatal error: Uncaught SoapFault exception

    Warning: SoapClient::SoapClient() expects parameter 2 to be array, boolean given in  login\login.php ...

  3. 24种设计模式--命令模式【Command Pattern】

    今天讲命令模式,这个模式从名字上看就很简单,命令嘛,老大发命令,小兵执行就是了,确实是这个意思,但是更深化了,用模式来描述真实世界的命令情况.正在看这本书的你,我猜测分为两类:已经工作的和没有工作的, ...

  4. js如何获取一个月的天数 data javascript

    js如何获取一个月的天数 function days(year,month){ var dayCount; now = new Date(year,month, 0); dayCount = now. ...

  5. linux安装composer

    1,确保php已成功安装,并且php可以被访问php -r "copy('https://getcomposer.org/installer', 'composer-setup.php'); ...

  6. 使用php发送电子邮件(phpmailer)

    在项目开发过程中,经常会用到通过程序发送电子邮件,例如:注册用户通过邮件激活,通过邮件找回密码,发送报表等.这里介绍几种通过PHP发送电子邮件的 方式(1)通过mail()函数发送邮件(2)使用fso ...

  7. Apache server-status

    1.找到apache配置文件:httpd.conf   2.打开模块: LoadModule status_module modules/mod_status.so   3.在文件末尾处加上以下代码: ...

  8. Python疑问系列

    最近在看python源码 ------点点滴滴做个记录. 预计要分的系列: 1. import 分析 2. 主要bytecode 分析 3. python启动分析 4. object对象分析 最后希望 ...

  9. u-boot Makefile Source Test

    一.概述 笔者已经写了一篇实现目标文件与源码分开的makefile测试实验,但是觉得不够完美,没有更多的体现u-boot Makefile的工作原理和特点.所以,决定重新修订,使之更加充分的接近u-b ...

  10. Scut:账号服务器问题修正

    姑且记录一下,以防未来出现bug回来看看今天改了哪些. 原 Scut 账服是应用于 渠道频道 的账号服务器,每天会发放大量的游客账号,它有一个"自动将已经被注册了一段时间的游客账号再重新推送 ...