论坛首页 移动开发技术论坛

在J2ME中模拟C语言中的文件操作

浏览 2665 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2008-02-27  
    最近在写一个模拟器(OR虚拟机),用于运行文曲星(一种电子词典)上的一种类C语言开发的GVmaker程序(不知道这里有没有玩过文曲星的,关于GVmaker可以看这里).做这个东西主要是怀念一下以前玩文曲星的日子.另外刚刚买了个智能手机,尝试一下J2ME的开发,这也是我第一个J2ME程序.
     这个东西并不复杂,而且因为刚学JAVA的时候写过一个桌面版的(只不过那个写的比较烂),所以构思好大体框架把各种接口定义好后就一路写过去.待到写文件系统这一块的时候才发现J2ME对文件的访问没有C语言中那么方便,也不像J2SE中有RandomAccessFile可以用.google了一下,也没有发现相关资料.于是我自己写了个在内存中模拟C语言文件操做.当然,通过内存模拟文件操作具有很大的局限性,消耗内存并且当文件过大的时候就不适用了.不过我这个就是为了模拟器中操作文件用的,而文曲星上的程序操作的文件都不会太大,所以这样用是没问题的.
     下面是实现的几个主要类:
内存文件的实现
/**
 * 虚拟文件,使用内存模拟文件操作<p>
 * 每个虚拟文件除了读写数据外,还有三个属性:position,limit,capacity<p>
 *     其capacity描述了这个虚拟文件当前能容纳的最大数据量,这个值不能从外部修改,但在向其写入数据时根据需要内部会适当扩充其capacity<p>
 *     limit描述的是虚拟文件当前存储的数据总量,外部可以读取或修改或增加数据到虚拟文件.这个值在调用readFromStream时自动初始化,并且内部自动维护<p>
 *     position表示下一个读/写数据的地址,相当于普通文件操作中的文件指针.其初始值应该由调用者在调用readFromStream方法后正确设置<p>
 * 对于一个含有数据,并正确初始化的VirtualFile,应有以下关系成立:<p>
 *     0<=position<=limit<=capacity
 * @author Eastsun
 * @version 2008-2-25
 */
public final class VirtualFile {

    //每次增量的最小值:128K
    private static final int MIN_ADD_CAPS = 0x20000;
    private static final int MAX_COUNT = 5;

    //内存块,最多支持5块,也就是640K,对GVmaker来说足够了
    private byte[][] bufs = new byte[MAX_COUNT][];
    //caps[k]表示第0,..k-1块内存的总容量
    private int[] caps = new int[MAX_COUNT + 1];
    //内存块数量
    private int count;
    //当前所在内存块下标
    private int index;
    //文件长度
    private int limit;
    //the index of the next element to be read or written
    private int position;

    /**
     * 使用一个初始容量构造VirtualFile
     * @param size
     */
    public VirtualFile(int size) {
        bufs[0] = new byte[size];
        for (int n = 1; n <= MAX_COUNT; n++) {
            caps[n] = size;
        }
        count = 1;
    }

    /**
     * 得到该VirtualFile总容量
     * @return capacity
     */
    public int capacity() {
        return caps[count];
    }

    /**
     * 得到VirtualFile中实际存储数据的长度,也就是文件的长度
     * @return length of file
     */
    public int limit() {
        return limit;
    }

    /**
     * 得到VirtualFile中的读写指针位置,也就是文件指针
     * @return position
     */
    public int position() {
        return position;
    }

    /**
     * 设置文件指针
     * @param newPos 新的指针位置
     * @return newPos 设置后的指针,若出错返回-1
     */
    public int position(int newPos) {
        if (newPos < 0 || newPos > limit) {
            return -1;
        }
        position = newPos;
        //修改index,使其满足caps[index]<=position<caps[index+1]
        while (caps[index + 1] <= position) {
            index++;
        }
        while (caps[index] > position) {
            index--;
        }
        return position;
    }

    /**
     * 读取文件数据,并且position加1
     * @return 当前position出的文件内容;若已到文件位(>=limit()),返回-1
     */
    public int getc() {
        if (position >= limit) {
            return -1;
        }
        int c = bufs[index][position - caps[index]] & 0xff;
        position++;
        if (position >= caps[index + 1]) {
            index++;
        }
        return c;
    }

    public int putc(int ch) {
        if (position > limit) {
            return -1;
        }
        ensureCapacity(position + 1);
        bufs[index][position - caps[index]] = (byte) ch;
        position++;
        if (position > limit) {
            limit = position;
        }
        if (position >= caps[index + 1]) {
            index++;
        }
        return ch;
    }

    /**
     * 将position,limit清零
     */
    public void refresh() {
        index = position = limit = 0;
    }

    /**
     * 从in读取数据到VirtualFile,初始limit的值,并设置position的值为0<p>
     * 操作完成后关闭in
     * @param in 数据来源
     * @throws java.io.IOException 发生IO错误
     */
    public void readFromStream(InputStream in) throws IOException {
        int size = in.available();
        limit = size;
        ensureCapacity(size);
        int n = 0;
        while (size > 0) {
            size -= in.read(bufs[n++]);
        }
        in.close();
    }

    /**
     * 将VirtualFile中的内容写入到out<p>
     * 操作完成后关闭out
     * @param out 写入目标
     * @throws java.io.IOException 发生IO错误
     */
    public void writeToStream(OutputStream out) throws IOException {
        int n = 0;
        while (limit > caps[n + 1]) {
            out.write(bufs[n++]);
        }
        if (limit > caps[n]) {
            out.write(bufs[n], 0, limit - caps[n]);
        }
        out.close();
    }
    //确保至少有minCap大小的内存可用
    private void ensureCapacity(int minCap) {
        if (caps[count] >= minCap) {
            return;
        }
        //每次至少增加128K
        int addCap = Math.max(MIN_ADD_CAPS, minCap - caps[count]);
        bufs[count] = new byte[addCap];
        for (int n = count + 1; n <= MAX_COUNT; n++) {
            caps[n] = caps[count] + addCap;
        }
        count++;
    }
}


模拟C文件操作
/**
 *
 * @author Eastsun
 * @version 2008-2-22
 */
public class DefaultFileModel extends FileModel {

    public static final String READ_MODE = "r";
    public static final String READ_PLUS_MODE = "r+";
    public static final String READ_B_MODE = "rb";
    public static final String READ_B_PLUS_MODE = "rb+";
    public static final String WRITE_MODE = "w";
    public static final String WRITE_PLUS_MODE = "w+";
    public static final String WRITE_B_MODE = "wb";
    public static final String WRITE_B_PLUS_MODE = "wb+";
    public static final String APPEND_MODE = "a";
    public static final String APPEND_PLUS_MODE = "a+";
    public static final String APPEND_B_MODE = "ab";
    public static final String APPEND_B_PLUS_MODE = "ab+";
    private static final int SEEK_SET = 0;
    private static final int SEEK_CUR = 1;
    private static final int SEEK_END = 2;
    //同时访问文件的最大个数
    private final static int MAX_FILE_COUNT = 3;
    private FileSys fileSys;
    private String workDir;
    private boolean[] canRead;
    private boolean[] canWrite;
    private String[] fileNames;
    //是否可用,也就是是否空闲
    private boolean[] usable;
    private VirtualFile[] files;
    //用于生成String的byte数组
    private byte[] strBuf;
    //用于保存屏幕缓冲区数据
    private Accessable graphBuffer;

    public DefaultFileModel(FileSys fileSys) {
        this.fileSys = fileSys;
        workDir = "";
        canRead = new boolean[MAX_FILE_COUNT];
        canWrite = new boolean[MAX_FILE_COUNT];
        usable = new boolean[MAX_FILE_COUNT];
        files = new VirtualFile[MAX_FILE_COUNT];
        fileNames = new String[MAX_FILE_COUNT];

        for (int index = 0; index < MAX_FILE_COUNT; index++) {
            usable[index] = true;
            //初始容量:64K
            files[index] = new VirtualFile(0x10000);
        }
        strBuf = new byte[200];
    }

    public boolean changeDir(Getable source, int addr) {
        String newDir = getFileName(source, addr);
        FileInf inf = fileSys.getFileInf(newDir);
        if (inf.isDirectory()) {
            workDir = newDir;
            return true;
        }
        else {
            return false;
        }
    }

    public boolean makeDir(Getable source, int addr) {
        String dir = getFileName(source, addr);
        return fileSys.makeDir(dir);
    }

    public boolean fileList(ScreenModel screen, KeyModel key, Setable dest, int addr) {
        return true;
    }

    public int fopen(Getable source, int fileName, int openMode) {
        int num = -1;
        //指示文件指针位置,true开头,false为结尾
        boolean pointer = true;
        //是否清除原有文件
        boolean clear = false;
        for (int index = 0; index < MAX_FILE_COUNT; index++) {
            if (usable[index]) {
                num = index;
                break;
            }
        }
        if (num == -1) {
            return 0;
        }
        String name = getFileName(source, fileName);
        String mode = getString(source, openMode);
        FileInf inf = fileSys.getFileInf(name);
        if (READ_MODE.equals(mode) || READ_B_MODE.equals(mode)) {
            if (!(inf.isFile() && inf.canRead())) {
                return 0;
            }
            canRead[num] = true;
            canWrite[num] = false;
        }
        else if (READ_PLUS_MODE.equals(mode) || READ_B_PLUS_MODE.equals(mode)) {
            if (!(inf.isFile() && inf.canRead() && inf.canWrite())) {
                return 0;
            }
            canRead[num] = true;
            canWrite[num] = true;
        }
        else if (WRITE_MODE.equals(mode) || WRITE_B_MODE.equals(mode)) {
            if (inf.isFile() && !inf.canWrite()) {
                return 0;
            }
            clear = true;
            canRead[num] = false;
            canWrite[num] = true;
        }
        else if (WRITE_PLUS_MODE.equals(mode) || WRITE_B_PLUS_MODE.equals(mode)) {
            if (inf.isFile() && !inf.canWrite()) {
                return 0;
            }
            clear = true;
            canRead[num] = true;
            canWrite[num] = true;
        }
        else if (APPEND_MODE.equals(mode) || APPEND_B_MODE.equals(mode)) {
            if (!(inf.isFile() && inf.canWrite())) {
                return 0;
            }
            canRead[num] = false;
            canWrite[num] = true;
            pointer = false;
        }
        else if (APPEND_PLUS_MODE.equals(mode) || APPEND_B_PLUS_MODE.equals(mode)) {
            if (!(inf.isFile() && inf.canRead() && inf.canWrite())) {
                return 0;
            }
            canRead[num] = true;
            canWrite[num] = true;
            pointer = false;
        }
        else {
            return 0;
        }
        VirtualFile file = files[num];
        if (clear) {
            file.refresh();
        }
        else {
            int length = 0;
            try {
                InputStream in = fileSys.getInputStream(name);
                length = in.available();
                file.readFromStream(in);
                in.close();
            } catch (IOException ex) {
                return 0;
            }
            file.position(pointer ? 0 : length);
        }
        fileNames[num] = name;
        usable[num] = false;
        return num | 0x80;
    }

    public void fclose(int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT) {
            return;
        }
        if (usable[fp]) {
            return;
        }
        if (canWrite[fp]) {
            try {
                OutputStream out = fileSys.getOutputStream(fileNames[fp]);
                files[fp].writeToStream(out);
            } catch (IOException e) {
            //do nothing
            }
        }
        usable[fp] = true;
    }

    public int getc(int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT) {
            return -1;
        }
        if (usable[fp] || !canRead[fp]) {
            return -1;
        }
        return files[fp].getc();
    }

    public int putc(int c, int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT) {
            return -1;
        }
        if (usable[fp] || !canWrite[fp]) {
            return -1;
        }
        return files[fp].putc(c);
    }

    public int fread(Setable dest, int addr, int size, int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT) {
            return -1;
        }
        if (usable[fp] || !canRead[fp]) {
            return -1;
        }
        VirtualFile file = files[fp];
        int count = 0, b;
        while (count < size && (b = file.getc()) != -1) {
            dest.setByte(addr++, (byte) b);
            count++;
        }
        return count;
    }

    public int fwrite(Getable source, int addr, int size, int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT) {
            return -1;
        }
        if (usable[fp] || !canWrite[fp]) {
            return -1;
        }
        VirtualFile file = files[fp];
        int count = 0, b;
        while (count < size) {
            b = source.getByte(addr++);
            if (file.putc(b & 0xff) == -1) {
                break;
            }
            count++;
        }
        return count;
    }

    public boolean deleteFile(Getable source, int addr) {
        return fileSys.deleteFile(getFileName(source, addr));
    }

    public int fseek(int fp, int offset, int base) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT || usable[fp]) {
            return -1;
        }
        VirtualFile file = files[fp];
        int pos = 0;
        switch (base) {
            case SEEK_SET:
                pos = offset;
                break;
            case SEEK_CUR:
                pos = file.position() + offset;
                break;
            case SEEK_END:
                pos = file.limit() + offset;
                break;
            default:
                return -1;
        }
        return file.position(pos);
    }

    public int ftell(int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT || usable[fp]) {
            return -1;
        }
        return files[fp].position();
    }

    public boolean feof(int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT || usable[fp]) {
            return true;
        }
        return files[fp].position() == files[fp].limit();
    }

    public void rewind(int fp) {
        fp &= 0x7f;
        if (fp >= MAX_FILE_COUNT || usable[fp]) {
            return;
        }
        files[fp].position(0);
    }

    public void dispose() {
        for (int index = 0; index < MAX_FILE_COUNT; index++) {
            fclose(index | 0x80);
        }
    }

    private String getFileName(Getable src, int addr) {
        String name = getString(src, addr);
        if (!name.startsWith("/")) {
            name = workDir + "/" + name;
        }
        return name;
    }

    private String getString(Getable src, int addr) {
        int length = 0;
        byte b;
        while ((b = src.getByte(addr++)) != 0) {
            strBuf[length++] = b;
        }
        return new String(strBuf, 0, length);
    }
}


其中用到的两个interface:
FileSys提供了与底层文件系统的交互接口
/**
 * 文件系统接口<p>
 * 注意:方法中涉及的文件名都是GVM中用到的文件名,不一定与底层实际文件一一对应.其解释由具体实现者提供<p>
 * @author Eastsun
 * @version 1.0
 */
public interface FileSys {

    /**
     * 得到该文件的InputStream,以读取其内容
     * @return in 当文件存在且canRead返回true时返回指向该文件的InputStream
     * @throws java.io.IOException 文件不存在或不可读或发生IO错误
     */
    public InputStream getInputStream(String fileName) throws IOException;

    /**
     * 得到该文件的OutputStream以向其写入内容<p>
     * 当文件不存在时会创建一个新的文件
     * @return out  返回指向该文件的OutputStream
     * @throws java.io.IOException 若文件不可写或发生IO错误
     */
    public OutputStream getOutputStream(String fileName) throws IOException;

    /**
     * 删除文件
     * @param fileName 文件名
     * @return true,如果删除成功
     */
    public boolean deleteFile(String fileName);

    /**
     * 建立文件夹
     * @param dirName 文件夹名
     * @return true,如果创建成功
     */
    public boolean makeDir(String dirName);

    /**
     * 得到指定文件/文件夹的相关信息<p>
     * @param fileName 文件名
     * @return 其相关信息
     */
    public FileInf getFileInf(String fileName);
}


FileInf提供了文件的相关信息
/**
 * 一个描述文件信息的接口
 * @author Eastsun
 * @version 1.0
 */
public interface FileInf {

    /**
     * 是否为一个文件
     */
    public boolean isFile();

    /**
     * 是否为一个文件夹
     * @return 当仅当存在且为文件夹时返回true
     */
    public boolean isDirectory();

    /**
     * 该文件夹或文件是否可读
     * @return 当且仅当存在且可读时返回true
     */
    public boolean canRead();

    /**
     * 该文件夹或文件是否可写
     * @return 当仅当存在且可写时返回true
     */
    public boolean canWrite();

    /**
     * 得到文件夹下文件个数
     * @return 为文件夹时返回其目录下文件个数(含子目录);否则返回-1
     */
    public int getFileNum();

    /**
     * 得到目录下第start个开始的num个文件名,保存到names中
     * @param names 用于保存文件名的String数组
     * @param start 开始文件号
     * @param num   个数
     * @return      实际得到的个数,如出错,返回-1
     */
    public int listFiles(String[] names, int start, int num);
}
  • src.rar (256.5 KB)
  • 描述: JGVM中的大部分源代码
  • 下载次数: 61
论坛首页 移动开发技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics