【问题标题】:Read data as arrays from a Text File in Java从 Java 中的文本文件中以数组形式读取数据
【发布时间】:2020-02-21 04:20:36
【问题描述】:

我有一个文本文件,里面有一堆数组,我必须在数组中找到一个特定的数字。文本文件如下所示:

(8) {1, 4, 6, 8, 12, 22}
(50) {2, 5, 6, 7, 10, 11, 24, 50, 65}
(1) {1}
(33) {1, 2, 5, 6, 11, 12, 13, 21, 25, 26, 30, 33, 60, 88, 99}
(1) {1, 2, 3, 4, 8, 9, 100}
(1) {2, 3, 5, 6, 11, 12, 13, 21, 25, 26, 30, 33, 60, 88, 99}

括号内的数字是我必须使用二进制搜索找到的数字。其余的是实际的数组。 我不知道如何从文本文件中获取此数组并能够将其作为实际数组读取。 [这是我之前参加的编码比赛的一个问题,正在复习这些问题]

我已经有了一种方法来进行二分搜索,并且我已经使用扫描仪来读取这样的文件:

Scanner sc = new Scanner(new File("search_race.dat"));

并使用了一个while循环来遍历文件并读取它。

但我被困在如何让 java 知道花括号中的内容是一个数组,而括号中的内容是它必须在所述数组上使用二进制搜索才能找到的内容。

【问题讨论】:

标签: java arrays file


【解决方案1】:

您可以逐行读取文件中的字符串,然后在每一行上使用正则表达式将字符串分成组。

下面的正则表达式应该适合匹配行

\((\d+)\) \{([\d, ]+)\}

然后 group(1) 将给出括号内的数字(作为字符串), group(2) 将给出大括号内的字符串,您可以使用 和空格(假设每个逗号后面都有一个空格)和获取一个数字数组(再次作为字符串)。

【讨论】:

    【解决方案2】:

    TL;DR:您必须逐个字符地检查它是花括号还是括号还是数字

    长答案:
    首先,使用int numberToFindArrayList<Integer> listOfNumbers 字段创建一个POJO(我们称之为AlgoContainer,但可以使用任何你喜欢的名称)。
    然后,像@ManojBanik has mentioned in the comments这样读取文件

    现在创建一个ArrayList<AlgoContainer>(它的大小应该与逐行读取文件时得到的ArrayList<String>相同)

    然后循环遍历上述步骤中的ArrayList<String>,进行如下操作:

    1. 创建并实例化一个AlgoContainer 对象实例(我们称之为tempAlgoContainer)。

    2. 检查第一个字符是否为开括号 -> 是吗?创建一个空的 temp String -> 检查下一个字符是否为数字 -> 是吗? -> 将其附加到空的String 并重复此操作,直到找到右括号。

    3. 找到左括号?将临时 String 解析为 int 并将 tempAlgoContainernumberToFind 字段设置为该数字。

    4. 接下来是大括号的东西:找到大括号了吗?创建一个新的空临时 String -> 检查下一个字符是否为数字 -> 是吗? append 然后将其附加到空的String 就像在步骤#2 中一样,直到找到逗号或右花括号。

    5. 找到逗号?将 temp String 解析为 int,然后将其添加到 tempAlgoContainerlistOfNumbers(这是一个字段)中 -> 使 temp String 再次为空。
    6. 找到一个右花括号?重复上述步骤,跳出循环。您现在已准备好处理您想做的任何事情。您的数据已准备就绪。

    另外,最好有一个AlgoContainer 的成员函数或实例方法(随便调用它)来执行二分搜索,这样您就可以简单地循环遍历ArrayList<AlgoContainer> 并调用该BS 函数它(没有双关语)

    【讨论】:

      【解决方案3】:

      要读取文件,可以使用Files.readAllLines()

      要真正解析每一行,你可以使用类似的东西。

      首先,为了方便起见,请删除行中的所有空格。

      line.replaceAll("\\s+", "");
      

      这实际上会将(8) {1, 4, 6, 8, 12, 22} 转换为(8){1,4,6,8,12,22}

      接下来,使用正则表达式来验证该行。如果该行不匹配,则无需进一步操作。

      表达式:\([0-9]*\)\{[0-9]*(,[0-9]*)*}

      • \([0-9]*\)(8) 相关(上例)
      • \{[0-9]*(,[0-9]*)*}{1,4,6,8,12,22} 相关

      如果您不理解该表达方式,请前往here

      最后,我们可以将字符串解析为两个部分:要搜索的数字和带有实际值的int[]

      // start from index one to skip the first bracket
      int targetEnd = trimmed.indexOf(')', 1);
      String searchString = trimmed.substring(1, targetEnd);
      // parsing wont throw an exception, since we checked with the regex its a number
      int numberToFind = Integer.parseInt(searchString);
      
      // skip ')' and '{', align to the first value, skip the last '}'
      String valuesString = trimmed.substring(targetEnd + 2, trimmed.length() - 1);
      // split the array at ',' to get each value as string
      int[] values = Arrays.stream(valuesString.split(","))
          .mapToInt(Integer::parseInt).toArray();
      

      解析完这两个组件后,您就可以自己进行二分搜索了。

      示例代码为Gist on GitHub

      【讨论】:

        【解决方案4】:

        您可以简单地解析每一行(要查找的数字和数组)如下:

        while (sc.hasNext()) {
            int numberToFind = Integer.parseInt(sc.next("\\(\\d+\\)").replaceAll("[()]", ""));
        
            int[] arrayToFindIn  = Arrays.stream(sc.nextLine().split("[ ,{}]"))
                                                .filter(x->!x.isEmpty())
                                                .mapToInt(Integer::parseInt)
                                                .toArray();
        
            // Apply your binary search ! Craft it by yourself or use a std one like below :
            // int positionInArray = Arrays.binarySearch(arrayToFindIn, numberToFind);
        }
        

        如果您不喜欢 replaceAll,可以将循环中的第一行替换为以下两行:

            String toFindGroup = sc.next("\\(\\d+\\)");
            int numberToFind = Integer.parseInt(toFindGroup.substring(1, toFindGroup.length()-1));
        

        干杯!

        【讨论】:

        • 您能详细说明一下 int[] arrayToFindIn 这行代码的作用吗?或者 Arrays.stream 是如何工作的?
        • 一旦我们用 Scanner next(...) 方法读取了第一个标记,调用 Scanner nextLine() 读取该行的其余部分。例如,这是您示例的第一个用例的字符串“{1, 4, 6, 8, 12, 22}”。然后,前一个字符串上的 split("[ ,{}]") 通过忽略提供的分隔符(即空格、逗号和大括号打开和关闭)返回一个字符串数组。问题是该数组包含空标记并包含字符串,而我们想要 int[]。这就是流电源发挥作用​​的地方。 Arrays.steam(T[]) 构建一个 T 流(按数组顺序排列的元素) - 见下一条评论
        • 在我们的例子中,我们有一个字符串流。通过链接操作,我们首先使用 filter(x->!x.isEmpty()) 删除所有空元素,然后使用 mapToInt(Integer::parseInt 将每个 String 元素映射/转换为 int ) 这会产生一个 IntStream。最后,通过终端操作toArray(),我们将该流中的元素返回为int []。请参阅以下文档:SplitArrays.stream 另请参阅 Stream 包。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-06
        • 1970-01-01
        相关资源
        最近更新 更多