好的,所以我正在开发基于java中的交易卡游戏的游戏。我将所有游戏peices的“信息”划分为一个csv文件,其中每一行都是游戏,每列都是该类别的一种属性。我花了几个小时用缓冲读取器等编写代码,试图将我的csv文件中的信息提取到二维数组但无济于事。我的csv文件链接在这里:http://dl.dropbox.com/u/3625527/MonstersFinal.csv我有一年的计算机科学,但我仍然无法弄清楚如何做到这一点。
所以我的主要问题是如何将它放入2D数组中,这样我可以保留行和列?
答案 0 :(得分:1)
好吧,如前所述,你的一些字符串包含逗号,所以最初你是从一个不好的地方开始的,但我确实有一个解决方案就是这样:
---------如果可能,重新调整网站,但执行简单的编码操作。你会想要做一些你会注意到在包含HTML的自动生成的XML文件中往往会做的事情;保留一个“控制字符”(一个可打印的字符效果最好,在这里,出于调试的原因和......好......理智),一旦编码,就永远不会被直接作为自身的实例读取。 Ampersand是我喜欢使用的,因为它不常见但仍然可打印,但实际上你想要使用的角色取决于你。我要做的是编写程序,以便在“,”的每个实例中,在写入CSV之前,该逗号将被“& c”替换,并且在该站点上的每个实例&符号上, “&安培;”将被“& a”取代。这样,你永远不会在CSV中意外地将单个值分成两个,并且你可以通过我将要概述的方法将它们分开后简单地解码每个值...
--------假设您知道每行中有多少列,您可以使用StringTokenizer类(查找它 - 它很棒并且内置到Java中。一个寻找的好地方信息一如既往地是Java教程),以数组的形式自动为您提供所需的值。
它的工作原理是你传入一个字符串和一个分隔符(在这种情况下,分隔符将是','),并且它会吐出由这些逗号分隔的所有子字符串。如果你知道从一开始就有多少件,你可以在开始时实例化一个2D数组,然后插入StringTokenizer给你的每一行。如果你不这样做,它仍然可以,因为你可以使用ArrayList。 ArrayList很不错,因为它是一个数组的更高级抽象,它会自动请求更多内存,以便您可以继续添加它并知道检索时间总是不变的。但是,如果您计划动态添加片段,并且比检索它们更频繁地执行操作,您可能希望使用LinkedList,因为它具有线性检索时间,但是比ArrayList更好的关系用于添加 - 删除时间。或者,如果你很棒,你可以改用SkipList。我不知道它们是否在Java中默认实现,但它们非常棒。但是公平的警告;检索,删除和放置的速度成本增加了内存方面的开销。跳过列表可以提供很多指示。
如果您知道每行中应该有相同数量的值,并且您希望它们按位置组织,但无论出于何种原因,您的刮刀都无法处理缺少某行的值,并且只是没有把这个值,你有一些坏消息......重写处理缺少值的刮刀代码部分会比编写解释变长数组的方法更容易为每个数组实例化一个Piece对象。我对此的建议再次是使用控制字符并用& n(对于'null')填充空列以便稍后解释,但是具体的当然是什么将个性化你的代码和编码风格所以它不适合我说。
编辑:我认为你应该关注的主要事情是学习Java中可用的不同标准库数据类型,并且可能学习自己实现其中一些用于练习。我记得实现了一个二叉搜索树 - 不是AVL树,但还好。它足够有趣,良好的编码实践,更重要的是,如果您希望能够快速有效地完成工作,那么这是必要的。我不知道Java究竟是如何实现数组的,因为定义是“连续的内存部分”,但你可以在运行时使用变量在Java中为它们分配内存......但无论具体的Java实现如何,数组通常都不是这是最好的解决方案。此外,了解正则表达式可以使所有更多更容易。对于练习,我建议将它们用于Java程序,或者,如果您不想每次都编译和包装,那么您的bash脚本(如果使用* nix)和/或批处理脚本(如果你“正在使用Windows。”
答案 1 :(得分:1)
我认为你抓取数据的方式使得这个问题比它需要的更困难。鉴于大多数值被引号不一致地包围,一些数据中已经有逗号,而且每张卡都不在其自己的行上,因此你的scrape似乎不一致且难以使用。
尝试以更加一致的格式重新抓取数据,例如:
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
类可以使数据处理更简单。
答案 2 :(得分:0)
我正在研究用于机器学习的类似问题,所以让我分享一下我在这个主题上可以做的事情。
1)如果您在开始解析行之前就知道了 - 它是否已经硬编码到您的程序中,或者您的文件中是否有一些标题可以为您提供此信息(强烈推荐) - 每行有多少属性将是,你可以用逗号合理地拆分它,例如第一个属性是RowString.substring(0,RowString.indexOf(',')),第二个属性将是从第一个逗号到下一个逗号的子串(编写一个函数来查找逗号的第n个实例,或者只是简单地删除字符串中的位,应该是相当简单的),最后一个属性将是RowString.substring(RowString.lastIndexOf(',') ,RowString.length())。 String类的方法是你的朋友。
2)如果您无法区分用于分隔值的逗号和作为字符串格式属性一部分的逗号,那么(如果文件足够小以便手动重新格式化)执行Java所做的事情 - 表示具有特殊含义的字符,在字符串内部使用'\,'而不仅仅是','。这样你就可以搜索“,”而不是“\”的索引,这样你就可以通过某种方式区分你的角色了。
3)作为2)的替代,CSVs(在我看来)不适合字符串,通常包括逗号。 CSV没有真正的通用格式,那么为什么不将它们设置为冒号分隔值,破折号分隔值,甚至是三个&符号分隔值?用逗号分隔值的目的是为了让它们易于区分,如果逗号不能完成工作则没有理由保留它们。同样,这仅适用于您的文件足够小以便手动编辑的情况。
4)查看您的文件不仅仅是格式,很明显您无法手动完成。此外,似乎某些字符串被三重双引号括起来(“”“string”“”),有些字符串被单个双引号(“string”)包围。如果我不得不猜测,我会说引号中包含的任何内容都是单个属性 - 例如,没有一对引号从一个属性开始而在另一个属性中结束。所以我想说你可以: 使用一种方法创建一个类,将字符串分解为每个以逗号分隔的字段。 编写该方法,使其忽略前面带有奇数个双引号的逗号(这样,如果引号对尚未关闭,则它知道它在字符串内并且逗号不是值分隔符)。但是,如果你的文件的创建者用双引号(“”string“”)括起一些字符串,那么这个策略就会失败,所以你可能需要一个更全面的方法。