忍者ブログ
  • 2024.10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 2024.12
[PR]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

【2024/11/23 01:06 】 |
iデバイスのファイルをMacに出力
iPhone,iPadにはファイラが無い。
データやファイルはすべてアプリに紐づけられる。
そのアプリで作られたデータは、そのアプリからしか見えない。
そのデータを他のアプリや他の機器から参照することはできない。

それは良いところでもあり、悪いところでもある。
あのデータはどこに置いたっけ…? と無くしもの探しをする必要がない。
データは必ず何らかのアプリに使われることで意味を持つ。そうでなければただの1と0のカタマリでしかない。
だから、データがアプリに管理されるというやり方は理に適っている。

その代わり、別のアプリからデータを参照したいとき、複数のアプリで連携したりなどはとても苦手である。ここはAndroidと正反対だ。
iPhoneとiPadのどちらでも動くアプリがあっても、中のデータは同期できないから、例えばiPhoneで進めたゲームをiPadで遊ぼうとしても、また一からやり直しになってしまう。

これはアプリ開発者にとっても起こる問題だ。

ゲームを作っていて、iPadで実機でステージエディットなんかしていて、さてこのデータをMacで見たいなとか、iPhoneに移したいな、というときに困ってしまう。
iPad内のアプリのホームディレクトリ下にエディットデータを保存しているけど、ファイラは無いから見えないし、同期をとってもMacにはコピーされない。どうしよう。


で、方法はいくつかある。

まず1つ。データを全部文字列に変換して、メールか何かで送る。
これは分かりやすい。
でも、バイナリファイルを文字列に変換する機能を作る必要がある。
毎回作るのは大変。

バイナリファイルをそのまま出力してしまえば、どんなファイルでも対応できる。

XCodeから転送したプログラムで実行しているときは、XCodeの標準出力が使える。
この場所は、iPhone,iPadからMacに対してデータを送れる数少ない場所である。
ここを使おう。

つまり、ホームディレクトリ下の全ファイルの中身を、コンソールに16進表示するコードをプログラム中に仕込んでおく。

で、これをバイナリファイルに戻してやれば良い。
これもプログラム中に仕込む必要がある。(iPhone,iPadにファイルを作るには、実機でプログラムを走らせて作成するしかないからね)


つまり、iPadからiPhoneにファイルをコピーするとすると、

ファイルの中身をコンソールに出力するプログラムを作る
iPadで実行
Mac-XCodeにファイルの中身が出力される
この出力をプログラム中に仕込み、ファイル出力するコードを書く
iPhoneでプログラム実行
iPhoneにファイル生成

という流れ。


まず、出力する方のコードはこう。

void outputAllFiles(NSString *path)
{
NSString *fullPath = [NSHomeDirectory() stringByAppendingPathComponent: path];

NSFileManager *fileMan = [NSFileManager defaultManager];
NSDirectoryEnumerator *dirEnum = [fileMan enumeratorAtPath:fullPath];

printf("\n\noutputAllFiles()\n\n");

NSString *file;
while (file = [dirEnum nextObject]) {

//ファイル名
const char *szFile = [file cStringUsingEncoding:NSUTF8StringEncoding];
printf("#%s\\n\\\n", szFile);

//ファイルの中身
NSData *loadedData = [NSData dataWithContentsOfFile:[fullPath stringByAppendingPathComponent:file]];
const void *bytes = [loadedData bytes];
size_t len = [loadedData length];

for (int i = 0; i < len; ++i) {
printf("%02x", *((const unsigned char *)bytes + i) & 0xff);
}
printf("\\n\\\n");
}
}


NSDirectoryEnumerator *dirEnum = [fileMan enumeratorAtPath:fullPath];
というのは、fullPath以下の全ファイルを列挙するオブジェクトdirEnumを作成している。
while (file = [dirEnum nextObject])
で次々にファイル名が取得できる。うう〜ん、べんり!
これは、親クラスであるNSEnumeratorクラスの機能。
ちなみに、
array = [dirEnum allObjects];
とすると、一度に全部のオブジェクトを取得できる(arrayはNSArray*型)。取得したあとのdirEnumは空っぽになる。
NSDirectoryEnumeratorについて

ファイル名の前には'#'を付けて見分けるようにし、
ファイルの中身は1バイトずつ16進2桁で表示。
これをすべてのファイルについて実行。

改行が"\\n\\\n"となっていることについては、あとで説明。
要するに、行末を "\n\"という形にしておきたいのだ。


さて、これを実行すると、コンソールにこんな感じで出力される。

#clear.dat\n\
000000000000000001000000bcbb1741
#files.dat\n\
7139315f303030342e64617400000000
#q91_0000.dat\n\
5b000000140000001400000000000000


これをプログラム中に仕込む。
要は、const char *変数を作っといて、そこの初期値にコピペするだけのこと。
行末をバックスラッシュにしてコンソールに出力しておいたのは、ここのため。
行をまたいでいても、一続きの文字列になるからね。


で、この長大な文字列をファイルに生成しなおすプログラムがこれ。

void createFiles(NSString *path)
{
//まず、文字列をtmpファイルに書き込む
NSString* tmpFileName = @"mytmpfile";
NSString* tmpFilePath = [[NSHomeDirectory() stringByAppendingPathComponent:path] stringByAppendingPathComponent:tmpFileName];
const char *szFilePath = [tmpFilePath cStringUsingEncoding:NSUTF8StringEncoding];

FILE *fp = fopen( szFilePath, "w+" );
if (!fp) {
printf("FileTransporter::createFiles() ファイルが開けなかった\n");
return;
}
fwrite(strFileTransporterData, strlen(strFileTransporterData), 1, fp);

//ファイル名を探す
fseek(fp, 0, SEEK_SET);

char buf[1024] = {0};

while (1){
if (fgets(buf, sizeof(buf)-1, fp) == NULL)
goto end;

//ファイル発見した場合
if (buf[0] == '#') {
char szNewFileName[80] = {0};
strncpy(szNewFileName, &buf[1], sizeof(szNewFileName));
if (szNewFileName[strlen(szNewFileName)-1] == '\n') {
szNewFileName[strlen(szNewFileName)-1] = '\0';
}
printf("file: %s\n", szNewFileName);

//新規ファイル作成
NSString* newFileName = [NSString stringWithCString:szNewFileName encoding:NSUTF8StringEncoding];
NSString* newFilePath = [[NSHomeDirectory() stringByAppendingPathComponent:path] stringByAppendingPathComponent:newFileName];
const char *szNewFilePath = [newFilePath cStringUsingEncoding:NSUTF8StringEncoding];

FILE *newFp = fopen(szNewFilePath, "w");
if (!newFp) {
printf("FileTransporter::createFiles() 新規ファイルが作成できなかった. %s\n", &buf[1]);
goto end;
}

printf(" writing...\n");

unsigned int val;
while (fscanf(fp, "%02x", &val) > 0){
//printf("%02x", byte);
unsigned char byte = val & 0xFF;
fwrite(&byte, 1, 1, newFp);
}
fclose(newFp);
printf(" finished.\n");
}
}
end:
fclose(fp);
}


まず、文字列を一時ファイルに書き出します。文字列のままだと作業がしにくいので。
ファイルに書き出して、行ごとに読み出すという方法で行きます。
ここが、コンソールに出力するときにわざわざ"\n"を付け足していた理由。

で、ファイル名を探します。目印は'#'でしたね。ファイル名に改行がついてしまわないよう気をつけながら、ファイルを新規作成します。
コンソールに出力していた16進2桁をバイトに直しながらチクチク書き込み。
これを全ファイルなくなるまで続けたらオシマイ。


※このプログラム、パス以下が階層構造になっている場合には対応していない。
今回はファイルが全部平に置いてあったので、これで十分だった。


今回、fgetsが使いたいがために、C言語のファイルの読み書きを使いました。
ずいぶん久しぶり…。最近は、fopen()でなくて、fopen_s()とか使うんですか?

errno_t errno = fopen_s(&fp, szFilePath, "w+");
if (errno == 0)
//成功
else
//失敗

こんな感じ?
PR
【2011/03/29 00:57 】 | iPhone | 有り難いご意見(0) | トラックバック()
<<正方形は長方形か? | ホーム | ファイルタイプ(言語)の変更>>
有り難いご意見
貴重なご意見の投稿














虎カムバック
トラックバックURL

<<前ページ | ホーム | 次ページ>>