1

好的,所以我正在开发基于 java 中的集换式卡牌游戏的游戏。我将所有游戏元素的“信息”抓取到一个 csv 文件中,其中每一行都是游戏元素,每一列都是该元素的一种属性。我花了几个小时用缓冲阅读器等编写代码,试图将我的 csv 文件中的信息提取到二维数组中,但无济于事。我的 csv 文件链接在这里:http ://dl.dropbox.com/u/3625527/MonstersFinal.csv我有一年的计算机科学专业,但我仍然无法弄清楚如何做到这一点。

所以我的主要问题是如何将它放入二维数组中,这样我就可以保留行和列?

4

3 回答 3

1

好吧,如前所述,你的一些字符串包含逗号,所以最初你从一个糟糕的地方开始,但我确实有一个解决方案,它是这样的:

  1. --------- 如果可能,重新抓取站点,但执行简单的编码操作。你会想要做一些你会注意到的事情,这些事情往往是在包含 HTML 的自动生成的 XML 文件中完成的;保留一个“控制字符”(一个可打印的字符在这里效果最好,出于调试和......好吧......理智的原因),一旦编码,就永远不会直接作为其自身的实例来读取。Ampersand 是我喜欢使用的,因为它不常见但仍然可以打印,但实际上你想使用什么字符取决于你。我要做的是编写程序,以便在“,”的每个实例中,在写入 CSV 之前,逗号将被替换为“&c”,并且在站点上实际与符号的每个实例中,“& " 将替换为 "&a"。

  2. -------- 假设您知道每行中有多少列,您可以使用 StringTokenizer 类(查找它 - 它很棒并且内置在 Java 中。查找信息的好地方是一如既往, Java 教程)以数组的形式自动为您提供所需的值。

    • 它的工作原理是您传入一个字符串和一个分隔符(在这种情况下,分隔符将是 ','),并且它会吐出由这些逗号分隔的所有子字符串。如果您从一开始就知道总共有多少块,您可以在开始时实例化一个 2D 数组,然后插入 StringTokenizer 提供给您的每一行。如果你不这样做,那也没关系,因为你可以使用 ArrayList。ArrayList 很好,因为它是一个更高级别的数组抽象,它会自动请求更多内存,这样您就可以继续添加它并且知道检索时间将始终保持不变。但是,如果您计划动态添加片段,并且比检索它们更频繁,您可能希望使用 LinkedList 代替,因为它具有线性检索时间,但在添加-删除时间方面比 ArrayList 的关系要好得多。或者,如果你很棒,你可以使用 SkipList 代替。我不知道它们是否在 Java 中默认实现,但它们很棒。不过,公平的警告;检索、删除和放置速度的成本伴随着内存开销的增加。跳过列表维护了很多指针。

    • 如果您知道每行中应该有相同数量的值,并且您希望它们按位置组织,但是无论出于何种原因,您的刮板都无法处理行中缺少值的情况,并且只是不放值,你有一些坏消息......重写刮板代码中处理缺少值的部分比编写一个解释可变长度数组并为每个数组实例化一个 Piece 对象的方法更容易大批。我对此的建议是再次使用控制字符并用 &n 填充空列(表示“null”),以便稍后解释,但是具体细节当然会区分你的代码和编码风格,所以我不该说.

编辑:我认为您应该关注的主要内容是学习 Java 中可用的不同标准库数据类型,并且可能学习自己实现其中的一些以进行练习。我记得实现了一个二叉搜索树——不是 AVL 树,但没关系。这很有趣,很好的编码实践,更重要的是,如果你想能够快速有效地做事,这是必要的。我不确切知道 Java 是如何实现数组的,因为定义是“内存的连续部分”,但是您可以在运行时使用变量在 Java 中为它们分配内存......但无论具体的 Java 实现如何,数组通常是不是最好的解决方案。此外,了解正则表达式会使一切变得更加重要更轻松。作为练习,我建议将它们放入您的 Java 程序中,或者,如果您不想每次都编译和 jar 东西,您的 bash 脚本(如果您使用 *nix)和/或批处理脚本(如果您'正在使用 Windows)。

于 2012-06-22T00:34:42.957 回答
1

我认为你抓取数据的方式使这个问题变得比它需要的更加困难。鉴于大多数值不一致地被引号包围,某些数据中已经包含逗号,并且并非每张卡都在自己的行中,因此您的抓取似乎不一致且难以处理。

尝试以更一致的格式重新抓取数据,例如:

R1C1|R1C2|R1C3|R1C4|R1C5|R1C6|R1C7|R1C8
R2C1|R2C2|R2C3|R2C4|R2C5|R2C6|R2C7|R3C8
R3C1|R3C2|R3C3|R3C4|R3C5|R3C6|R3C7|R3C8
R4C1|R4C2|R4C3|R4C4|R4C5|R4C6|R4C7|R4C8
A/D Changer|DREV-EN005|Effect Monster|Light|Warrior|100|100|You can remove from play this card in your Graveyard to select 1 monster on the field. Change its battle position.

每行绝对是它自己的卡片(与您在奇数位置发布的带有新行的示例 CSV 不同),并且分隔符永远不会在数据字段中用作分隔符以外的东西。

一旦您将输入变为一致可读的状态,解析它就变得非常简单:

    BufferedReader br = new BufferedReader(new FileReader(new File("MonstersFinal.csv")));
    String line = "";

    ArrayList<String[]> cardList = new ArrayList<String[]>(); // Use an arraylist because we might not know how many cards we need to parse.

    while((line = br.readLine()) != null) { // Read a single line from the file until there are no more lines to read
        StringTokenizer st = new StringTokenizer(line, "|"); // "|" is the delimiter of our input file.
        String[] card = new String[8]; // Each card has 8 fields, so we need room for the 8 tokens.
        for(int i = 0; i < 8; i++) { // For each token in the line that we've read:
            String value = st.nextToken(); // Read the token
            card[i] = value; // Place the token into the ith "column"
        }
        cardList.add(card); // Add the card's info to the list of cards.
    }

    for(int i = 0; i < cardList.size(); i++) {
        for(int x = 0; x < cardList.get(i).length; x++) {
            System.out.printf("card[%d][%d]: ", i, x);
            System.out.println(cardList.get(i)[x]);
        }
    }

这将为我给定的示例输入产生以下输出:

card[0][0]: R1C1
card[0][1]: R1C2
card[0][2]: R1C3
card[0][3]: R1C4
card[0][4]: R1C5
card[0][5]: R1C6
card[0][6]: R1C7
card[0][7]: R1C8
card[1][0]: R2C1
card[1][1]: R2C2
card[1][2]: R2C3
card[1][3]: R2C4
card[1][4]: R2C5
card[1][5]: R2C6
card[1][6]: R2C7
card[1][7]: R3C8
card[2][0]: R3C1
card[2][1]: R3C2
card[2][2]: R3C3
card[2][3]: R3C4
card[2][4]: R3C5
card[2][5]: R3C6
card[2][6]: R3C7
card[2][7]: R4C8
card[3][0]: R4C1
card[3][1]: R4C2
card[3][2]: R4C3
card[3][3]: R4C4
card[3][4]: R4C5
card[3][5]: R4C6
card[3][6]: R4C7
card[3][7]: R4C8
card[4][0]: A/D Changer
card[4][1]: DREV-EN005
card[4][2]: Effect Monster
card[4][3]: Light
card[4][4]: Warrior
card[4][5]: 100
card[4][6]: 100
card[4][7]: You can remove from play this card in your Graveyard to select 1 monster on the field. Change its battle position.

我希望在这里重新抓取信息是一种选择,我希望我没有误解任何东西;祝你好运!

最后一点,一旦你把事情解决了,别忘了利用 OOP。一个Card类可以使处理数据更加简单。

于 2012-06-22T03:50:15.863 回答
0

我正在研究用于机器学习的类似问题,所以让我分享一下我在该主题上能够做的事情。

1)如果您在开始解析该行之前就知道 - 它是否被硬编码到您的程序中,或者您的文件中是否有一些标题可以为您提供此信息(强烈推荐) - 每行会有多少个属性,你可以合理地用逗号分割它,例如第一个属性将是 RowString.substring(0, RowString.indexOf(',')),第二个属性将是从第一个逗号到下一个逗号的子字符串(编写一个函数找到逗号的第 n 个实例,或者只是在遍历它时切掉字符串的位,应该相当简单),最后一个属性将是 RowString.substring(RowString.lastIndexOf(','), RowString.长度())。String 类的方法在这里是你的朋友。

2)如果您在区分用于分隔值的逗号和作为字符串格式属性的一部分的逗号时遇到问题,那么(如果文件小到可以手动重新格式化)执行 Java 所做的 - 用字符串中带有 '\,' 而不仅仅是 ',' 的特殊含义。这样您就可以搜索“,”而不是“\”的索引,这样您就可以通过某种方式来区分您的字符。

3) 作为 2) 的替代方案,CSV(在我看来)不适用于通常包含逗号的字符串。CSV 没有真正的通用格式,那么为什么不将它们设为冒号分隔值、破折号分隔值,甚至三连字符分隔值呢?用逗号分隔值的目的是为了便于区分它们,如果逗号不起作用,就没有理由保留它们。同样,这仅适用于您的文件小到可以手动编辑的情况。

4)查看您的文件不仅仅是格式,很明显您无法手动完成。此外,似乎有些字符串被三个双引号 ("""string""") 包围,而一些字符串被单双引号 ("string") 包围。如果我不得不猜测,我会说引号中包含的任何内容都是单个属性 - 例如,没有一对引号以一个属性开头并以另一个属性结尾。所以我想说你可以: 使用一种方法创建一个类,将字符串分成每个逗号分隔的字段。编写该方法,使其忽略前面有奇数个双引号的逗号(这样,如果引号对尚未关闭,它就知道它在字符串中并且逗号不是值分隔符)。这种策略,

于 2012-06-21T23:52:15.670 回答