【问题标题】:Parsing multi line records using java 8 streams使用 java 8 流解析多行记录
【发布时间】:2015-05-26 21:30:50
【问题描述】:

我正在尝试解析以下文件,其中包含以下格式的信息:

表名

VARIABLE_LIST_OF_COLUMNS

VARIABLE_NUMBER_OF_ROWS(由制表符分隔)

一个例子(使用','作为问题的分隔符;实际的分隔符是一个制表符):

学生

身份证

名字

1,迈克

2,金佰利

这个想法是建立一个插入sql语句的列表(代码sn-p的上下文)。

我想知道的是,这种多行解析是否完全可以使用 java 8 流 API?这是我目前拥有的:

public final class StatementGeneratorMain {

    public static void main(final String[] args) throws Exception{
        List<String> fileNames = Arrays
            .asList("STUDENTS.txt");
        fileNames.stream()
            .forEach(fileName -> {
                String tableName;
                List<String> columnNames;
                List<String[]>  dataRows;
                try (BufferedReader br = getBufferedReader(fileName)) {
                    tableName = br.lines().findFirst().get();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                try (BufferedReader br = getBufferedReader(fileName)) {
                    //skip the first line because its been processed.
                    columnNames = br.lines().skip(1).filter(v -> v.split("\t").length == 1).collect(toList());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                try (BufferedReader br = getBufferedReader(fileName)) {
                    //skip the first line and the columns length to get the data
                    //columns are identified as being splittable on the delimiter
                    dataRows = br.lines().skip(1 + columnNames.size()).map(s -> s.split("\t"))
                        .collect(toList());
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                String columns = columnNames.stream().collect(joining(",","(",")"));

                List<String> dataRow = dataRows.stream()
                    .map(arr -> Arrays.stream(arr).map(x -> "'" + x + "'").collect(joining(",", "(", ")")))
                    .map(row -> String.format("INSERT INTO %s %s VALUES %s;", tableName, columns, row))
                    .collect(toList());

                dataRow.forEach(l -> System.out.println(l));
            });
    }

    private static BufferedReader getBufferedReader(String fileName) {
        return new BufferedReader(new InputStreamReader(StatementGeneratorMain.class.getClassLoader().getResourceAsStream(
            fileName)));
    }
}

这段代码为我完成了这项工作,但我真的不喜欢它,因为我三次读取同一个文件(一次用于表名,再次推断列,再次获取行)。我也不认为这是正确的功能风格。

我正在寻找一种更优雅的方式来使用流 API 进行这种多行/多记录解析。

为了完整起见,输出为:

INSERT INTO STUDENTS (ID, NAME) 值 ('1','Mike');

INSERT INTO STUDENTS (ID, NAME) 值 ('2','Kimberly');

此时我对数值列和空值之类的东西不太讲究。

【问题讨论】:

  • 如果您的代码有效并且您正在寻找改进它的方法,那么您可能应该在codereview.stackexchange.com 而不是 Stack Overflow 上发布您的问题。
  • 顺便说一句,我不确定您为什么需要 getBufferedReader 方法。如果您想从文件中获取行流,只需使用Files.lines(Paths.get(fineName))(如果需要,您也可以添加字符集)。
  • @Pshemo 有很好的建议。我要补充一点,如果列名是一行上的 CSV(而不是单独一行上的每个列名),您的生活和代码会更简单,因为您实际上需要将它们作为 CSV 并且它解决了弄清楚的问题列名停止而行开始的位置。
  • 顺便说一句,Bobby Tables 很喜欢你引用数据库输入的方式。
  • @Pshemo 谢谢,我想我会在那里回答问题。

标签: java parsing java-8 java-stream


【解决方案1】:

我不确定在这里使用流是否是正确的方法,因为它们意味着用于迭代数据一次,或者更准确地说,以一种方式处理数据。如果您需要以不同方式处理单独的数据块,您可能应该使用良好的旧循环或迭代器。想到的最简单的解决方案之一是使用 Scanner,因此您的代码如下所示:

Pattern oneWordLine = Pattern.compile("^\\w+$", Pattern.MULTILINE);

List<String> files = Arrays.asList("input.txt");
for (String file : files) {

    try (Scanner sc = new Scanner(new File(file))) {

        String tableName = sc.nextLine();

        StringJoiner columnNamesJoiner = new StringJoiner(", ", "(", ")");
        // iterate over lines with single words
        while (sc.hasNext(oneWordLine)) {
            columnNamesJoiner.add(sc.nextLine());
        }
        String columns = columnNamesJoiner.toString();


        List<String> dataRow = new ArrayList<>();
        // iterate over rest of lines
        while (sc.hasNextLine()) {
            String values = Arrays.stream(sc.nextLine().split("\t")) 
                    .collect(joining("', '", "('", "')"));
            dataRow.add(String.format("INSERT INTO %s %s VALUES %s;", 
                    tableName,columns, values));
        }

        dataRow.forEach(System.out::println);

    } catch (Exception e) {
        e.printStackTrace();// no need to rethrow RuntimeEception
    }
}

【讨论】:

    【解决方案2】:

    您可以将这块“BufferedReader br = getBufferedReader(fileName)”移到上面,并根据需要阅读。我不认为,它需要阅读三遍。

    【讨论】:

      猜你喜欢
      • 2018-05-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-04
      • 2019-04-28
      • 2012-04-14
      • 1970-01-01
      • 2016-03-13
      相关资源
      最近更新 更多