有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

使用java解析csv,然后保存在2D数组中

好的,我正在开发一个基于java的交易卡游戏的游戏。我将所有游戏peice的“信息”刮到一个csv文件中,其中每一行都是游戏peice,每一列都是该peice的一种属性。我花了好几个小时用缓冲读取器等编写代码,试图将csv文件中的信息提取到2d数组中,但没有成功。我的csv文件链接在这里:http://dl.dropbox.com/u/3625527/MonstersFinal.csv我有一年的计算机科学经验,但我仍然不知道如何做到这一点

所以我的主要问题是如何将其放入2D数组中,这样就可以保留行和列


共 (3) 个答案

  1. # 1 楼答案

    我正在研究一个类似的问题,用于机器学习,所以让我分享一下我在这个主题上所做的工作

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

    2)如果在区分用于分隔值的逗号和作为字符串格式属性一部分的逗号时遇到困难,那么(如果文件足够小,可以手动重新格式化)执行Java所做的操作——用“\”表示字符串中具有特殊含义的字符,而不只是“,”。这样你就可以搜索“,”而不是“\”的索引,这样你就可以用某种方式来区分你的字符

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

    4)查看你的文件不仅仅是格式,很明显你不能手工完成。此外,有些字符串似乎被三个双引号(“字符串”)包围,有些字符串被单双引号(“字符串”)包围。如果我不得不猜测,我会说引号中包含的任何内容都是单个属性——例如,没有以一个属性开头、以另一个属性结尾的引号对。所以我想说你可以: 用一个方法创建一个类,将一个字符串拆分为每个逗号分隔的字段。 编写该方法时,它会忽略前面带有奇数双引号的逗号(这样,如果引号对尚未闭合,它就会知道它在字符串中,并且逗号不是值分隔符)。然而,如果文件的创建者做了一些类似于用双引号(““string”)括起一些字符串的事情,这种策略就会失败,因此您可能需要更全面的方法

  2. # 2 楼答案

    正如前面提到的,有些字符串包含逗号,所以一开始你是从一个不好的地方开始的,但我有一个解决方案,它是这样的:

    1. -如果可能的话,重新扫描站点,但在重新扫描时执行简单的编码操作。你会想做一些事情,比如你会注意到在自动生成的包含HTML的XML文件中所做的事情;保留一个“控制字符”(出于调试和…嗯…理智的原因,这里的可打印字符效果最好),它一旦编码,就永远不能作为自身的实例直接读取。我喜欢使用符号,因为它很少见,但仍然可以打印,但真正想用什么字符取决于你自己。我要做的是编写程序,这样,在“,”的每个实例中,在写入CSV之前,逗号都会被“&;c”替换,在站点上的每个实际的“与”实例中,逗号都会被“&将替换为“&a”。这样,你就永远不会有在CSV中意外地将一个值分成两个的问题,你可以简单地在用我将在中概述的方法将每个值分开后对它们进行解码

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

      • 它的工作原理是传入一个字符串和一个分隔符(在本例中,分隔符应该是“,”),并输出由这些逗号分隔的所有子字符串。如果你从一开始就知道总共有多少块,你可以在开始的时候实例化一个2D数组,然后插入StringTokenizer给你的每一行。如果不这样做,也没关系,因为可以使用ArrayList。ArrayList很好,因为它是一个数组的高级抽象,可以自动请求更多内存,这样您就可以继续添加到它,并且知道检索时间总是恒定的。然而,如果您计划动态添加片段,并且这样做的频率比检索它们的频率更高,那么您可能会希望使用LinkedList,因为它具有线性检索时间,但是对于添加-删除时间,它的关系比ArrayList好得多。或者,如果你很棒的话,你可以用一个能工巧匠来代替。我不知道它们是否默认在Java中实现,但它们非常棒。不过,这是一个合理的警告;检索、删除和放置速度的代价伴随着内存开销的增加。跳过列表维护很多指针

      • 如果你知道每一行中应该有相同数量的值,并且你希望它们按位置组织,但是不管出于什么原因,你的刮板不能处理一行中缺少值的问题,只是没有把这个值放进去,那么你有一些坏消息。。。重写scraper代码中处理缺少值的部分要比编写一个解释变长数组并为每个数组实例化一个片段对象的方法容易得多。我的建议是再次使用控制字符,并用&;n(代表'null')将在稍后解释,但具体细节当然会使您的代码和编码风格个性化,所以我不想说

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

  3. # 3 楼答案

    我认为你搜集数据的方式让这个问题变得更加困难。考虑到大多数值被不一致的引号包围,有些数据中已经有逗号,而且不是每张卡都在自己的行中,你的刮卡看起来不一致,很难处理

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

    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类可以使处理数据变得更加简单