IOSがバイナリデータファイルを読み取る
プロジェクト開発の過程で、上図のようなデータが出てくる場合があります。実際、これらのデータはファイルに格納されたバイナリデータです。大学時代、先生が簿記ソフトを作ってくれたのを覚えていますが、当時、請求書のデータはファイルに保存されていました。その時の様子を簡単に説明します。まず、請求クラスが必要です。
------------------請求書タイプのヘッダーファイル-------------------
//
// Payout.h
// ノートをとる
//
// 12-12-3にLiu Chao によって作成さ れました。
// Copyright(c)2012 NianLiu Chao 。無断複写・転載を禁じます。
//
#import <Foundation / Foundation.h>
#define KAmountKey @ "Amount"
#define KTimekey @ "Time"
#define KTypeKey @ "Type"
#define KCommentKey @ "コメント"
@interface Payout : NSObject < NSCoding 、NSCopying >
@property (assign,nonatomic)float amount;
@property (strong,nonatomic)NSDate * time;
@property (strong,nonatomic)NSString * type;
@property (strong,nonatomic)NSString * comment;
-(NSString*)setMoth:(NSDate*)datetime;
-(NSString*)getType;
@end
------------------账单类的原文件-----------------
//
// Payout.m
// NoteTaking
//
// Created by 刘超 on 12-12-3.
// Copyright (c) 2012年刘超. All rights reserved.
//
#import "Payout.h"
@implementation Payout
@synthesize amount;
@synthesize time;
@synthesize type;
@synthesize comment;
-(void)encodeWithCoder:(NSCoder *)encoder
{
[encoder encodeFloat:amountforKey:KAmountKey];
[encoder encodeObject:timeforKey:KTimekey];
[encoder encodeObject:typeforKey:KTypeKey];
[encoder encodeObject:commentforKey:KCommentKey];
}
-(id)initWithCoder:(NSCoder *)decoder
{
if (self=[superinit]) {
amount=[decoderdecodeFloatForKey:KAmountKey];
time=[decoderdecodeObjectForKey:KTimekey];
type=[decoderdecodeObjectForKey:KTypeKey];
comment=[decoderdecodeObjectForKey:KCommentKey];
}
returnself;
}
-(id)copyWithZone:(NSZone *)zone
{
Payout*copy=[[[selfclass]allocWithZone:zone]init];
copy.amount=amount;
copy.time=[self.timecopyWithZone:zone];
copy.type=[self.typecopyWithZone:zone];
copy.comment=[self.commentcopyWithZone:zone];
return copy;
}
-(NSString*)setMoth:(NSDate*)datetime
{
//datetime= self.time;
NSDateFormatter*atime=[[NSDateFormatteralloc]init];
[atime setDateStyle:NSDateFormatterMediumStyle];
[atime setTimeStyle:NSDateFormatterShortStyle];
[atime setDateFormat:@"MM"];
NSString*moth=[atimestringFromDate:self.time];
return moth;
}
@end
このクラスの各メンバー変数は、<NSCoping>プロトコルに従います。なぜこの合意を実施するのですか?データをファイルに書き込むため、問題は実行する必要があるコピープロセスです。1つのデータをコピーします。コピー後の2つのデータは互いに影響しません。これらは独立したクローンです。一方のコピーのデータが変更されても、もう一方のコピーのデータは互いに影響しません。これは、<NSCopying>プロトコルを実現する必要があります :( 上記のid )copyWithZone :( NSZone *)ゾーンメソッドまたは<NSMutableCopying>合意:( 上記のid )mutableCopyWithZone :( NSZone *)ゾーン。次のステップは、データをファイルに書き込み、それをアプリケーションサンドボックスに保存することです。まず、データを格納するためのパスが必要ですが、パスの取得方法は以下の通りです。
#define KFilenme @ "aehive"
-(NSString *)dataFilepath
{
NSArray * paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory 、NSUserDomainMask 、YES );
NSString * documentDirectory = [paths objectAtIndex :0];
return [documentDirectorystringByAppendingPathComponent:KFilenme];
}
接着我们就是要有两个最重要的方法来进行文件的读写操作。我就直接上代码了。
-(void) writeList:(NSMutableArray *) list To:(NSString *) fileName//写的函数
{
NSMutableData*data=[[NSMutableDataalloc]init];//声明一个可变数据类型
NSKeyedArchiver*archiver=[[NSKeyedArchiveralloc]initForWritingWithMutableData:data];//编码
[archiver encodeObject:listforKey:KDataKey];//存放数据文件的类型和名字
[archiver finishEncoding];//编码完成
[data writeToFile:[selfdataFilepath]atomically:YES];//写在相应的路径下
}
-(NSMutableArray *) readListFromFile:(NSString *) fileName//读的函数
{
if ([[NSFileManagerdefaultManager]fileExistsAtPath:fileName])//是否存在要读的数据类型,如果存在
{
NSData*data=[[NSMutableDataalloc]initWithContentsOfFile:fileName];//打开名为fileName数据类型的数据
NSKeyedUnarchiver*unarchiver=[[NSKeyedUnarchiveralloc]initForReadingWithData:data];//解码
NSMutableArray * array = [unarchiverdecodeObjectForKey:KDataKey];//把解码出来的数据存放在可变数组里面
[unarchiver finishDecoding];//解码完成
if (!array) {
array=[[NSMutableArrayalloc]init];
}
return array;//返回该数组
}
return [[NSMutableArrayalloc]init];;//如果不存在名为fileName数据类型直接返回空
}
可以看出这种把数据写到文件中,其实就是利用NSKeyedArchiver归档(将各种类型的对象存到文件中) ,编码后直接调用- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)useAuxiliaryFile;写到文件中就可以了。读出来的时候也是调用相应的函数读出来,然后解码。这个存进去的数据就可以完全读出来了。写到这里,可能大家会认为关于文件的读写就完了。其实今天的重点还没有开始。
我们在iOS中NSKeyedArchiver方法来归档数据。数据的读写相对容易得多了。有时候我们拿到的文件是其它平台把一个已经定义好了的结构体等数据以二进制的方式存放到文件中。问题就来了这时候怎么读数据呢?其实这时候我们就要用到下面的方法来对数据进行读的操作。这些数据存储空间里面是有相应的长度的,我们知道了该结构体的定义,就可以知道该结构体的长度,然后就可以把这些数据一个字节一个字节的拷贝出来。这需要我们iOS端有与之相应的结构体。下面这段代码就是对一份文件中存了很多个相同结构的结构数据拷贝出来。大概思想就是我们做了一个循环来读每一个结构体的数据,只要拷贝的总字节小于文件的总字节数。就说明这个数据还没有读到末尾。还用了一个变量来纪录目前读过的总字节数。具体代码如下。
NSString *path = [[NSBundlemain Bundle]pathForResource:@"n9gps"ofType:@"rbb"];
//获取数据
NSData *reader = [NSDatadata WithContentsOfFile:path];
//得到文件的长度(大小)
NSInteger nSize = [reader length];
NSInteger nPos =0;
NSMutableDictionary*dataDic=[[NSMutableDictionaryalloc]init];
char* pBuffer = [readerbytes];
while (nPos<nSize) {
structRMBDM_FRAMEHEADER frameHeader={0};
memcpy(&frameHeader, pBuffer+nPos,sizeof(RMBDM_FRAMEHEADER));
nPos += sizeof(RMBDM_FRAMEHEADER);
structRMBDM_DATETIME dataTime={0};
structRMBDM_GPS dataGps={0};
memcpy(&dataTime, pBuffer+nPos,sizeof(RMBDM_DATETIME));
nPos += sizeof(RMBDM_DATETIME);
memcpy(&dataGps, pBuffer+nPos,sizeof(RMBDM_GPS));
nPos += sizeof(RMBDM_GPS);
NSString*time=[NSStringstringWithFormat:@"%d-%d-%d %d:%d:%d",dataTime.stuDateTime.cYear,dataTime.stuDateTime.cMonth,dataTime.stuDateTime.cDay,dataTime.stuDateTime.cHour,dataTime.stuDateTime.cMinute,dataTime.stuDateTime.cSecond];
N9MRmdbGpsInfo* rmdbGpsInfo=[[N9MRmdbGpsInfoalloc]init];
rmdbGpsInfo.cVersion=[NSStringstringWithFormat:@"%d",dataGps.cVersion];
rmdbGpsInfo.cGpsSource=[NSStringstringWithFormat:@"%d",dataGps.cGpsSource];
rmdbGpsInfo.reserved1=[NSStringstringWithFormat:@"%d",dataGps.reserved1];
rmdbGpsInfo.cGpsStatus=[NSStringstringWithFormat:@"%d",dataGps.cGpsStatus];
rmdbGpsInfo.cSpeedUnit=[NSStringstringWithFormat:@"%d",dataGps.cSpeedUnit];
rmdbGpsInfo.usSpeed=dataGps.usSpeed;
rmdbGpsInfo.cDirectionLatitude=[NSStringstringWithFormat:@"%d",dataGps.cDirectionLatitude];
rmdbGpsInfo.cDirectionLongitude=[NSStringstringWithFormat:@"%d",dataGps.cDirectionLongitude];
rmdbGpsInfo.cLongitudeCent=[NSStringstringWithFormat:@"%d",dataGps.cLongitudeCent];
rmdbGpsInfo.lLatitudeSec=dataGps.lLatitudeSec;
rmdbGpsInfo.lLongitudeSec=dataGps.lLongitudeSec;
rmdbGpsInfo.usGpsAngle=dataGps.usGpsAngle;
rmdbGpsInfo.cGpPlanetNum=[NSStringstringWithFormat:@"%d",dataGps.cGpPlanetNum];
rmdbGpsInfo.cBdPlanetNum=[NSStringstringWithFormat:@"%d",dataGps.cBdPlanetNum];
rmdbGpsInfo.cSignalStrength=[NSStringstringWithFormat :@ "%d" 、dataGps。cSignalStrength ];
[dataDic setObject :rmdbGpsInfo forKey :time];
}