ひとモノBLOG

11 « 2017 / 12 » 01
Sun Mon Tue Wed Thu Fri Sat
- - - - - 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
31 - - - - - -
profile
SHOO
  • SHOO
  • 機械系の学生。の割にプログラミングとかのほうが得意だったり。
  • RSS
links
BLOG内検索
Adds by DTI Blog

TangoのVFSをちょっといじってみた。
2008/09/23(火) [17:53:15]

Tangoの仮想フォルダシステムをちょっといじってみました。
まともに使えたら、強力かもしれない。
VFSは、仮想的にファイルシステムを作り、たとえば実際のディレクトリやFTP、WebDav、zipファイル、仮想ファイルなどなどを、同様に扱えるようにするためのシステムです。
クロスプラットフォームなファイルシステムとか、設定ファイルをまとめてアーカイブにしたりとかしようと思った場合に便利かもしれません。
このVFSを使っていろいろと試してみました。

VFSには、フォルダとファイルがあります。
基本的にはこれらの使い方が分かれば、あとはZIPだろうがFTPだろうが同じように扱うことができるというわけです。

フォルダ

VfsFolder というインターフェースを使います。 VfsFolder にはショートネームとロングネームがあるらしい。
ショートネームは .name ロングネームは .toString でそれぞれ得ることができます。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;

void main()
{
    // 実際のフォルダにリンクしたVFSフォルダを作成
    VfsFolder f = new FileFolder("test");
    // ショートネームとロングネームの確認
    Stdout.formatln("[{}]{}", f.name, f.toString);
}

[test]test
ちょっと触った感じ、ショートネームはフォルダ名、ロングネームはフルパスのような感覚のようです。

ちなみに、 FileFolder は実際のフォルダを利用した VfsFolder の実装のようです。
実際のフォルダがない場合、
tango.core.Exception.VfsException: FileFolder.open :: path does not exist: test

みたいなエラーメッセージが出ます。
この場合、コンストラクタの第二引数にtrueとか与えると自動的に作成してくれます。

フォルダの中のフォルダにアクセスする場合はこのようにします。
import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;

void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // フォルダの中のchildフォルダのVFSフォルダを取得
    auto child = folder.folder("child").open;
    Stdout.formatln("[{}]{}", child.name, child.toString);
}

[child]test/child
注意点は、openをしないと、実際のフォルダは開くことができません。
これは、 .folder から返るのが VfsFolderEntry というインターフェースであるからで、フォルダを開くopen以外に、existsでフォルダの存在を確認したり、createでフォルダを新しく作成したりといった事を可能とするためです。

ロングパスで直接アクセスすることもできます。
import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;

void main()
{
    // 実際のフォルダにリンク
    VfsFolder child = new FileFolder("test/child");
    Stdout.formatln("[{}]{}", child.name, child.toString);
}

[child]test/child
フォルダ内の情報を取得するには、 VfsFolders というインターフェースを用います
VfsFolders では、 files を使ってファイルの数をカウントしたり、フォルダ内のファイルの合計バイト数を調べたり、パターンマッチでファイルやフォルダを検索することができます。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;

void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // フォルダ内の情報を取得
    auto info = folder.self;
    Stdout(info.classinfo.name).newline;
    Stdout.formatln("file count: {}", info.files);
    Stdout.formatln("folders count: {}", info.folders);
    Stdout.formatln("content size (of files): {}", info.bytes);
}

file count: 1
folders count: 1
content size (of files): 1

とりあえず .self を使うと、そのフォルダ自身のみを含んだ VfsFolders が返ります。
これは、再帰的には検索せず、そのフォルダの中身についてのみ情報を提供します。
再帰的に検索する場合はこのようにします。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;

void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // フォルダ内の情報を取得
    auto info = folder.tree;
    Stdout.formatln("file count: {}", info.files);
    Stdout.formatln("folders count: {}", info.folders);
    Stdout.formatln("content size (of files): {}", info.bytes);
}

file count: 2
folders count: 2
content size (of files): 3

.self を .tree にするだけです。
また、パターンマッチも使う場合はこうです。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // フォルダ内の情報を取得
    // 再帰的に検索し、 c から始まるものだけを取得する
    auto info = folder.tree.subset("c*");
    Stdout.formatln ("file count: {}", info.files);
    Stdout.formatln ("folders count: {}", info.folders);
    Stdout.formatln ("content size (of files): {}", info.bytes);
}

file count: 1
folders count: 1
content size (of files): 2

VfsFolders の、 .subset を使います。
ここではワイルドカードを利用できます。

VfsFolders から、 VfsFolder を取得するには、 foreach を使います。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // フォルダ内を再帰的に検索し、各フォルダに対してVfsFolderを取得
    foreach (f; folder.tree)
    {
        Stdout.formatln("[{}]{}", f.name, f.toString);
    }
}

[test]test
[child]test/child

ファイル

次は、フォルダ内のファイルにアクセスしてみます。
使うインターフェースは VfsFile です。
このインターフェースは .file で得ることができます。
あと、ファイルにもフォルダ同様、ショートネームとロングネームがあります。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // test/a.txtのファイルを取得
    auto file = folder.file("a.txt");
    // とりあえずショートネーム、ロングネーム、ファイルサイズを表示
    Stdout.formatln("[{}]{}ntsize:{}", file.name, file.toString, file.size);
}

[a.txt]test/a.txt
    size:1

ファイルの読み方/書き方です。
.inputによって入力ストリームが、.outputによって出力ストリームが開けます。
呼び出すたびに開いてしまいますので、LineInputやFormatOutput等を使って最後にきちんと閉じてやるのがいいでしょう。
import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
import tango.io.stream.LineStream;
import tango.io.stream.FormatStream;
void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // test/a.txtのファイルを取得
    auto file = folder.file("a.txt");
    // ストリームから読み出す
    auto fin = new LineInput(file.input);
    foreach (line; fin)
    {
        Stdout(line).newline;
    }
    fin.close;
    // ストリームに書き出す
    scope fout = new FormatOutput(file.output, true);
    fout.formatln("a");
    fout.flush.close;
}

a

注意点としては、ストリームを閉じる際に
file.input.close; などとしてはいけないということです。
なぜなら、file.inputで開いてしまうので、二重に開こうとしてエラーが発生します。


VfsFile の機能は、他にも簡単なファイル操作を行うことが可能です。
ファイルの存在の確認 .exists
ファイルを移動する .move
ファイルを作成する .create
ファイルを削除する .remove
等を行えます。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
void main()
{
    // 実際のフォルダにリンク
    VfsFolder folder = new FileFolder("test");
    // test/a.txtのファイルを取得
    auto fileA = folder.file("a.txt");
    auto fileB = folder.file("b.txt");
    assert(fileA.exists);
    assert(!fileB.exists);
    // fileAをfileBに移動(改名)
    fileB.move(fileA);
    assert(!fileA.exists);
    assert(fileB.exists);
    // fileBをfileAにコピー
    fileA.copy(fileB);
    assert(fileA.exists);
    assert(fileB.exists);
    // fileBを削除
    fileB.remove;
    assert(fileA.exists);
    assert(!fileB.exists);
    Stdout.formatln("complete!");
}

complete!

ドライバ

上記はすべて実際のファイルシステムで行いましたが、VFSの利点は実際のフォルダシステムでなくても同じように扱うことができる点です。
その代表として、とりあえず ZipFolder があります。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.ZipFolder;
void main()
{
    // zipファイルをフォルダとしてリンク
    VfsFolder folder = new ZipFolder("test.zip");
    // test.zip内のe.txtファイルを取得
    auto file = folder.file("e.txt");
    auto ist = file.input;
    Stdout.stream.copy(ist);
    Stdout.newline;
    ist.close;
}

eeeee
次に、仮想フォルダ。
VirtualFolder を利用します。
仮想的にフォルダを作成し、その中に実際のフォルダなどを mount して子フォルダにします。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
import tango.io.vfs.VirtualFolder;
void main()
{
    // フォルダとリンク
    VfsFolder folder1 = new FileFolder("test");
    VfsFolder folder2 = new FileFolder("test2");
    // 仮想フォルダの定義
    VfsHost folder = new VirtualFolder("folder");
    // 仮想フォルダにフォルダをリンク
    folder.mount(folder1).mount(folder2);
    // 仮想フォルダ内の*.txtファイルを再帰的に列挙
    foreach (f; folder.tree.catalog("*.txt"))
    {
        Stdout.formatln("[{}]{}", f.name, f.toString);
    }
}

[c.txt]test2/c.txt
[d.txt]test2/d.txt
[a.txt]test/a.txt
[b.txt]test/child/b.txt

注意点としては、 VfsFolder では mount ができないので、 VfsHost にしなければならない点か。
まぁ、今回はいちいち型を書いていますけど、実際にはauto等とすればあまり問題はありませんね。

次に、リンクフォルダ。
複数のフォルダをコンバインして、同じフォルダとして扱うことができるようになります。

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
import tango.io.vfs.LinkedFolder;
void main()
{
    // フォルダとリンク
    VfsFolder folder1 = new FileFolder("test");
    VfsFolder folder2 = new FileFolder("test2");
    // 仮想フォルダの定義
    VfsHost folder = new LinkedFolder("folder");
    // 仮想フォルダにフォルダをリンク
    folder.mount(folder1).mount(folder2);
    // 仮想フォルダ内の*.txtファイルを再帰的に列挙
    auto fileA = folder.file("a.txt");
    auto fileB = folder.file("child/b.txt");
    auto fileC = folder.file("c.txt");
    auto fileD = folder.file("d.txt");
    Stdout.formatln("[{}]{}", fileA.name, fileA.toString);
    Stdout.formatln("[{}]{}", fileB.name, fileB.toString);
    Stdout.formatln("[{}]{}", fileC.name, fileC.toString);
    Stdout.formatln("[{}]{}", fileD.name, fileD.toString);
}

[a.txt]test/a.txt
[b.txt]test/child/b.txt
[c.txt]test2/c.txt
[d.txt]test2/d.txt

いまのところ、Tangoではこの程度のことができるようです。
ただ………………

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
import tango.io.vfs.LinkedFolder;
import tango.io.vfs.VirtualFolder;
import tango.io.vfs.ZipFolder;
void main()
{
    auto folder1 = new FileFolder("test");
    auto vfolder = new VirtualFolder("virtual");
    vfolder.mount(folder1);
    Stdout.formatln("{}", vfolder.file("test/a.txt"));
}

test//a.txt
なんか / が一個多い…?
import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.FileFolder;
import tango.io.vfs.LinkedFolder;
import tango.io.vfs.VirtualFolder;
import tango.io.vfs.ZipFolder;
void main()
{
    auto folder1 = new FileFolder("test");
    auto lfolder = new LinkedFolder("linked");
    auto vfolder = new VirtualFolder("virtual");
    lfolder.mount(folder1);
    vfolder.mount(lfolder);
    Stdout.formatln("{}", vfolder.file("linked/a.txt"));
}

tango.core.Exception.VfsException: '' is not a recognized member of 'linked'
???

import tango.io.Stdout;
import tango.io.vfs.model.Vfs;
import tango.io.vfs.VirtualFolder;
import tango.io.vfs.ZipFolder;
void main()
{
    auto folder1 = new ZipFolder("test.zip");
    auto vfolder = new VirtualFolder("virtual");
    vfolder.mount(folder1, "test");
    Stdout.formatln("{}", vfolder.file("test/e.txt"));
}

object.Exception: Access Violation
俺が何をした…?


まだきちんと整備されてないんだろうか…?
それともおれの使い方が悪いのか…?
わからない……


rel="prev"<<半年ぶりくらいの更新 | ホーム |

この記事に対するトラックバック

この記事のトラックバックURL

この記事に対するコメント

この記事にコメントする

管理者にだけ表示を許可する
| HOME |