【问题标题】:How do we know if a record has already been read?How do we follow record pointers?我们如何知道一条记录是否已经被读取?我们如何跟踪记录指针?
【发布时间】:2013-11-30 19:11:05
【问题描述】:

我对与处理文件相关的算法感到困惑。
根据我的在线研究,表示可变长度记录的标准方法是将元数据放在数据中,例如记录的长度。此外,如果我们想按某种顺序保存记录,我们还可以将指向下一条记录的指针存储为记录字段的一部分。
让我们以一个文件为例,其中我们有<length,data,next_record_ptr> 形式的记录,这与我上面描述的一致。
我看到的代码 sn-ps 通常会在内存缓冲区中读取文件的一部分,我猜这很有意义。示例:

ByteBuffer copy = ByteBuffer.allocate(SIZE);  
FileChannel fc = FileChannel.open(file, READ, WRITE);  
int nread;  
do {  
   nread = fc.read(copy);  
} while (nread != -1 && copy.hasRemaining());  

我认为这或多或少是一种读取内存中文件部分的方法。 SIZE 的大小可以包含我们的多个记录。

问题:当我们在内存中有 X 条记录时,我们如何知道我们要读取的特定记录是否在我们的缓冲区中?示例:如果我有指向下一条记录的指针,我如何知道该特定记录已作为缓冲区的一部分加载,或者我必须从文件中读取更多数据?
这部分在教科书中被提到很简单,但我对这实际上是如何实现的感到困惑。

明确一点:用 Java 提供示例代码真的很棒!!!但是用伪代码或清晰的算法对我来说清楚地解释这个黑点也很棒!

更新:
为了更清楚:
如果我已经读取了数组 [N] 中的 N 条记录。如果我阅读记录 array[0] 然后得到 array[0].pointerToNext 这是例如0x123A 在文件中。我怎么知道:
1) 如果位置0x123A中的记录已在缓冲区中读取,即array[n]
2) 这条记录在缓冲区中的索引是多少?

【问题讨论】:

  • 您需要在读取文件时以某种方式解释数据。读取长度和/或指针并跟踪读取了多少字节和/或读取器的位置以找到起点。您还可以将某种标识符存储为数据的一部分。
  • @Radiodef:您的意思是以某种方式将缓冲区大小与记录指针相关联?
  • 是的,你可以这样做。要么这样做,要么让它足够大,以使整个记录适合存在重叠的缓冲区。或分段阅读记录。但最重要的是以某种方式跟踪这一切。 AFAIK 所有 InputStream 衍生物都有一个带有长度参数的读取,因此如果您已将长度存储在文件中,则不一定需要重叠读取。
  • @Radiodef:我有点迷失你了。如果我在缓冲区中索引 0 处的记录具有下一条记录 0x1234 的指针的值,我怎么知道该文件位置中的记录是否在缓冲区中读取?
  • 从文件中流的位置中减去它,看看它的前面或后面有多远。如果记录没有按顺序出现在文件中,最好的方法是 IMO 以某种方式寻找:stackoverflow.com/questions/3792747/…Java 不幸的是通常没有有效的寻找方法。

标签: java file file-io io nio


【解决方案1】:

在文件中,记录指针通常存储为记录索引或文件位置。因此,您可以根据您在文件中的当前位置推断您是否已经阅读了特定记录。

在您的示例中,您可以使用变量 pos 来跟踪文件位置:

int pos = 0;
int nread;  
do {  
   nread = fc.read(copy);
   if(nread == -1) {
      break;
   } else {
      pos += nread;
   }
} while (copy.hasRemaining());  

【讨论】:

  • 在您的代码中pos 之后的while 循环位于文件的部分之后 我们读取的部分。 pos 中的这个位置如何帮助确定已读取的特定记录的位置?
  • 另外如何使用pos找到缓冲区中的索引?
  • 那么您的问题是:“如果缓冲区中已读取位置 0x123A 中的记录”。对此的检查将是 pos > 0x123A。根据您(重新)使用缓冲区的方式,您可能只使用缓冲区位置而不是显式跟踪文件位置。
【解决方案2】:

您需要随时读取数据,并将其纳入您的读取中。这是解决这个问题的一种方法,也许我会这样做。

// read a made up database
// untested

public class SimpleDatabaseRead {

    public void main(String[] args) {
        if (args == null || args.length == 0) {
            System.out.println("no file");
            return;
        }

        try {
            File file = new File(args[0]);
            FileInputStream in = null;

            try {
                in = new FileInputStream(file);

                // rec length will be 16-bit unsigned
                // large array OK, only 64KiB in mem
                byte[] bytes = new byte[65536];

                int bread = 0;
                long fpos = 0, next = 0;

                char[][] rec;

                while ((bread = in.read(bytes, 0, 4)) != -1) {
                    fpos += bread;

                    // length of this rec in bytes
                    int len = ((bytes[0] & 0xFF) << 8) | (bytes[1] & 0xFF);

                    // num entries in rec, for convenience
                    int entries = ((bytes[2] & 0xFF) << 8) | (bytes[3] & 0xFF);

                    if (in.read(bytes, 0, len) != len)
                        break;
                    fpos += len;

                    rec = new char[entries][];

                    for (int i = 0, k = 0, h; i < entries; i++) {
                        rec[i] = new char[
                            ((bytes[k++] & 0xFF) << 8) | (bytes[k++] & 0xFF)];

                        for (h = 0; h < rec[i].length && k < len; h++)
                            rec[i][h] = ((bytes[k++] & 0xFF) << 8) | (bytes[k++] & 0xFF);
                    }

                    for (char[] rec : recs)
                        System.out.println("record: " + new String(rec));

                    if (in.read(bytes, 0, 4) != len)
                        break;
                    fpos += bread;

                    // 48-bit file marker of next rec
                    next = ((bytes[0] & 0xFF) << 32)
                         | ((bytes[1] & 0xFF) << 24)
                         | ((bytes[2] & 0xFF) << 24)
                         |  (bytes[3] & 0xFF);

                    if (next < fpos) {
                        in.close();
                        in = new FileInputStream(file);
                        in.skip(fpos = next);
                    }
                }

            } finally {
                if (in != null) in.close();
            }
            System.out.println("read ended");

        } catch (IOException e) {
            System.out.println(e.getMessage());
        } catch (FileNotFoundsException e) {
            System.out.println(e.getMessage());
        }
    }
}

据我所知,在 Java 中并没有真正的好方法来寻求。 Seeking a ByteArrayInputStream using java.io 有一些想法,但要点是基本上你要么:

  • 如示例中所示创建一个新的 InputStream 或
  • 使用据称通常较慢的 RandomAccessFile(自行测试)。

换句话说,您最好使用可以顺序读取的数据。

我猜不是像您建议的那样以另一种方式逐块读取,而是将缓冲区大小设置为任意长度并随时制定记录。要判断下一个点是否在“缓冲区中”,您将读取指针并基本上使用条件pointer &lt; fpos + buffer.length。我不认为我会推荐这种方法,因为缓冲区可能小于记录的长度。在这种情况下,您最终会在新数组中制定记录,因此您不妨阅读整个内容。除非它们非常非常大,并且您必须部分阅读这些块。

【讨论】:

  • +1:我想我明白你的意思,但我不确定你在用位设置做什么。特别是因为括号是错误的,例如int len = ((bytes[0] &amp; 0xFF) &lt;&lt; 8) | (bytes[1] &amp; 0xFF);
  • 他们是对的。 &amp; 0xFF 是字节值的掩码。在移位之前这是必要的,否则字节会通过符号扩展自动提升为整数。如果您在换档前不进行掩码,您将得到非常虚假的结果。这是一个显示相同内容的链接:nayuki.eigenstate.org/page/javas-signed-byte-type-is-a-mistake 此页面上的第一张图显示了如何将字节符号扩展为 int:sagarsiddhpura.blogspot.com/2010/08/sign-extension-in-java.html 尽管该页面是关于右移的。
  • 但是你为什么要这样做呢? entries的号码是多少?
  • 条目数是为了方便实例化值数组。存储元素的数量和字符串长度可以让您非常方便地存储所有不同长度的字符串数组。
  • 这是一个非常有用的答案。实际上,您现在的回答让我开始思考在我们的应用程序中定义页面和块是否是标准做法,类似于操作系统。在这里查看我的其他(相关)困惑stackoverflow.com/questions/20017744/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-16
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
相关资源
最近更新 更多