【问题标题】:File Writer Java ; how to overwrite文件编写器 Java ;如何覆盖
【发布时间】:2012-06-29 10:31:20
【问题描述】:

我正在为我的游戏制作高分实现。这就是我想做的事情: 我有包含字符串名称和整数分数的对象分数。 现在:

  • 如果文件中没有名称,请添加它
  • 如果文件中有名称,则在空格后取出字符串并转换为整数,这样我就得到了分数。

现在,如果分数比实际分数好,我必须在文件上覆盖它... 这是我的问题..我可以这样做吗?如何在文件的某个点将一个字符串准确地写入另一个字符串?

【问题讨论】:

  • 你真的需要在同一个地方覆盖文本还是只需要记录新的高分?例如使用属性可能是最简单的,但这不会保留条目的顺序。

标签: java file writer


【解决方案1】:

对于这种需求,通常认为替换文本文件中的文本过于繁琐,因此通常的做法是读入整个文件,进行替换并编写整个文件的新版本。如果您有大量数据,您可以使用数据库或 NoSQL 解决方案。

附:考虑使用序列化,它可以让事情变得更简单。

【讨论】:

  • 我还建议在您的用例中使用序列化。通过使用可序列化的集合实现并使包含的对象可序列化,让 Java 平台为您完成繁重的工作。这样,您所需要做的就是将您的集合写入 ObjectOutputStream 并完成。
  • @Jarkko 对象序列化不如Properties 文件、文本或数据库那么健壮(跨版本)。
  • @AndrewThompson 同意。虽然它非常简单,但我经常在数据丢失不是关键因素的情况下使用它。显然,SQLite(或任何其他)数据库也适用于此处描述的使用方法。
  • 失去了我最喜欢的高分。游戏是一个关键因素。
【解决方案2】:

我有一个包含字符串名称和整数分数的对象分数。

请改用Properties 文件。它提供了一个简单的界面来加载和保存文件、获取键(名称)以及设置或检索值(分数)。 String 值被存储,但它们可以很容易地转换为整数。

【讨论】:

    【解决方案3】:

    我同意这最好通过完全重新序列化整个数据库来完成。在现代计算机上,您可以为每个磁盘推送 30MB/s 的线性写入(如果有足够的缓存则更多)。如果您要处理超过 30MB 的数据,那么您真的需要一个数据库(HSQLDB、DerbyDB、BerkleyDB),这些都是微不足道的数据库。或者一路去postgres/mysql。

    但是,覆盖现有文件的 FIXED 大小部分的方法(或者更确切地说,模拟这样做的方法)是使用:

    RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
    try {
      raf.seek(position);
      raf.writeInt(newScore);
      raf.close();
    } finally { raf.close(); }
    

    注意使用 writeInt 而不是 raf.write(Integer.toHexString(newScore).getBytes()),因为你真的需要固定它的大小。

    现在,如果文本文件本质上是 ascii(例如,人类将读取文件),因此该值不能是二进制的。也许您可以保留它 HexString(因为它的大小是固定的),或者您可以一个零填充的十进制字符串:

    但是你绝对不能做的就是将字符串增加 1 个字节。 所以:

    bob 15
    joe 7
    nina 981
    

    不能将 joe 的分数替换为 10,除非您填充了一堆空格。

    如果这是您的数据文件,那么您绝对必须重写整个文件(即使您编写的额外代码仅从更改点开始重写 - 从统计上讲,这将是文件的 50% 和因此不值得打扰。

    另一件事 - 如果你重写,你有缩短文件的风险.. 为此你需要打电话 raf.setLength(0); 在写入第一个字节之前。否则,您会在新文件末尾看到幻影文本。

    【讨论】:

      【解决方案4】:
      final Map<String,Long> symbol2Position = new ConcurrentHashMap<>();
      final Map<String,Integer> symbol2Score    = new ConcurrentHashMap<>();
      final String fileName;
      final RandomAccessFile raf;
      // ... skipped code
      void storeFull() {
           raf.position(0);
           raf.setLength(0);
           for (Map.Entry<String,Long> e : symbol2Position.entrySet()) {
      
              raf.writeUTF8(e.getKey());
              raf.write(',');
              symbol2Score.put(e.getKey(), raf.position());
              raf.writeUTF8(String.format("%06d",e.getValue()));
              raf.write('\n');
           }
      }
      void updateScore(String key, int newScore) {
          if (symbol2Score.containsKey(key)) {
             symbol2Score.put(key, newScore);
             raf.position(symbol2Position.get(key));
             raf.writeString(String.format("%06d", newScore));
          } else {
             symbol2Score.put(key, newScore); 
             long fileLen = raf.length();
             symbol2Position.put(key, fileLen);
             raf.position(fileLen);
             raf.writeString(String.format("%06d", newScore));
          }
      }
      

      我可能更愿意使用 DB 或二进制映射文件。意思是每个字段 8B 的文件,4B 指向用户名位置,4B 表示分数。但这允许人类可读的数据文件,同时使更新比仅仅重写属性文件更快。

      查看 LevelDB - 地球上最快的嵌入式系统数据库。 :) 它的主要功能是每秒数千/数百万次更新,而无需在多 GB 文件中随机更新 6 个字节的 rand-seek-rewrite 成本。

      【讨论】:

        【解决方案5】:

        只是一个想法,名称和分数的文件存储有什么具体原因吗?

        似乎Map&lt;String, Integer&gt; 会更好地为您服务...

        【讨论】:

        • 显然高分需要持久化。
        • 是的,这对我来说没用
        猜你喜欢
        • 2017-06-18
        • 1970-01-01
        • 2012-04-15
        • 1970-01-01
        • 1970-01-01
        • 2013-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多