【问题标题】:Facing Issue in Parsing .csv file using Java [closed]使用Java解析.csv文件时面临的问题[关闭]
【发布时间】:2021-01-16 14:19:06
【问题描述】:

我有一个 .csv 文件,如下所示。它有 2 列 URL 和 timestamp 。假设 URL 是字母数字和 UTC 日期/时间。

URL,timestamp
URL1,2018-12-09T14:19:00+00:00
URL1,2018-12-09T10:13:00+00:00
URL2,2018-12-09T07:25:00+00:00
URL3,2018-12-09T06:19:00+00:00
URL2,2018-12-08T22:03:00+00:00
URL3,2018-12-08T21:30:00+00:00
URL3,2018-12-08T09:30:00+00:00
URL2,2018-12-07T23:30:00+00:00

我想查找给定日期作为输入,访问次数最多的 URL。

eg : Input : 2018-12-09
     Output : URL1
    Explaination: We can see above that URL1 has 2 entries on 2018-12-09 ,so it was visited the most times on that day.

我实现上述的想法是使用HashMap<String , List<String>> 来存储date as Key 和URL's as List of values for that Key。

我在解析此文件时遇到问题。我该怎么做

  1. 解析时间戳列并将日期存储为 Key 在 Map 中,因为有重复的日期?

  2. 我假设一旦我们可以缩小上述范围,我们可以将 URL 作为值存储在该键的列表中。我们可以从映射中提取对应键的值到一个数组,然后检查大多数重复的 URL。这是正确的做法吗?

我没有为此使用任何 3rd 方库

我们非常感谢您对以上内容的任何意见。

【问题讨论】:

  • 只是谷歌如何使用 Java 逐行读取文件。非常直接。
  • 看来您正在寻找多集。如果只计算每个 URL 的出现次数会怎样?
  • @fluffy 我们可以统计 URL 的出现次数,但是我们如何确定该 URL 在哪一天访问次数最多?
  • @Dale 当然。我只是想把我的逻辑放在这里并得到任何建议。
  • 您想要哪个时区的日期?在所有时区都不会是同一个日期,午夜(理解为日期更改时)通常发生在不同时区的不同时间。

标签: java csv datetime parsing hashmap


【解决方案1】:

我建议您创建一个自定义类型,例如Visit 如下图所示,以结构化的方式处理记录。您可以use the standard Java I/O library 或一些专门的库从文件中读取记录并填充Visit 对象。为此,我建议您使用流行的opencsv library。无论您以何种方式填充Visit 对象并创建这些对象的List<Visit>,您都可以使用Java Stream API 来处理该列表。为了这个演示,我已经 manullay 创建了一个List<Visit>,其中包含您问题中显示的记录。

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

class Visit {
    private String url;
    private OffsetDateTime dateTime;

    public Visit(String url, OffsetDateTime dateTime) {
        this.url = url;
        this.dateTime = dateTime;
    }

    public String getUrl() {
        return url;
    }

    public OffsetDateTime getDateTime() {
        return dateTime;
    }
}

public class Main {
    public static void main(String[] args) {
        List<Visit> list = List.of(
                new Visit("URL1", OffsetDateTime.parse("2018-12-09T14:19:00+00:00")),
                new Visit("URL1", OffsetDateTime.parse("2018-12-09T10:13:00+00:00")),
                new Visit("URL2", OffsetDateTime.parse("2018-12-09T07:25:00+00:00")),
                new Visit("URL3", OffsetDateTime.parse("2018-12-09T06:19:00+00:00")),
                new Visit("URL2", OffsetDateTime.parse("2018-12-08T22:03:00+00:00")),
                new Visit("URL3", OffsetDateTime.parse("2018-12-08T21:30:00+00:00")),
                new Visit("URL3", OffsetDateTime.parse("2018-12-08T09:30:00+00:00")),
                new Visit("URL2", OffsetDateTime.parse("2018-12-07T23:30:00+00:00"))

        );

        // Tests
        System.out.println(mostVisitedUrlByDate(list, LocalDate.of(2018, 12, 9)));
        System.out.println(mostVisitedUrlByDate(list, LocalDate.now(ZoneOffset.UTC)));
        System.out.println(mostVisitedUrlByDate(null, LocalDate.of(2018, 12, 9)));
    }

    static String mostVisitedUrlByDate(List<Visit> list, LocalDate date) {
        return list!=null ? list.stream() // Traverse list if it is not null
            .filter(e -> e.getDateTime().toLocalDate().equals(date))
            // Get a Map<String, List<Visit>> where value is the list of Visit objects grouped on the URL
            .collect(Collectors.groupingBy(Visit::getUrl))
                .values()
                // Stream of values of Map obtained as a result of grouping 
                .stream()
                // Find the value i.e. List<Visit> of largest size
                .max(Comparator.comparing(List::size))
                //A List<Visit> with a default Visit object  
                .orElse(List.of(new Visit("Unknown", OffsetDateTime.now(ZoneOffset.UTC))))
                // All Visit elements in the value have same URL. Get the URL of the first value
                .get(0) 
                .getUrl()
                // Return "Unknown" if list is null
                :"Unknown";
    }
}

输出:

URL1
Unknown
Unknown

我在代码中放了足够多的 cmets,以便您更容易理解。另外,我对方法mostVisitedUrlByDate进行了三个测试调用,以便您清楚地了解Stream和Optional上每个方法调用的目的。

【讨论】:

    【解决方案2】:

    我创建了一个 CSV 文件,其中包含您问题中的示例数据,并将其命名为 recorder.csv。

    使用 Java stream API 和接口 Multiset - 来自 Google guava 库 - 正如 @fluffy 在他的 comment 中所建议的那样。
    (代码后的解释。)

    import com.google.common.collect.HashMultiset;
    import com.google.common.collect.Multiset;
    
    import java.io.IOException;
    import java.nio.file.Files;
    import java.nio.file.Path;
    import java.nio.file.Paths;
    import java.time.LocalDate;
    import java.time.format.DateTimeFormatter;
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Optional;
    import java.util.function.BiConsumer;
    import java.util.function.Supplier;
    
    public class MostHits {
    
        public static void main(String[] args) {
            LocalDate search = LocalDate.parse("2018-12-09", DateTimeFormatter.ISO_LOCAL_DATE);
            Path path = Paths.get("recorder.csv");
    
            Supplier<Map<LocalDate, Multiset<String>>> supplier = () -> new HashMap<>();
    
            BiConsumer<Map<LocalDate, Multiset<String>>, String> accumulator = (map, line) -> {
                String[] flds = line.split(",");
                LocalDate key = LocalDate.parse(flds[1], DateTimeFormatter.ISO_OFFSET_DATE_TIME);
                Multiset<String> multiset;
                if (map.containsKey(key)) {
                    multiset = map.get(key);
                }
                else {
                    multiset = HashMultiset.create();
                }
                multiset.add(flds[0]);
                map.put(key, multiset);
            };
    
            BiConsumer<Map<LocalDate, Multiset<String>>, Map<LocalDate, Multiset<String>>> combiner =
                    (map1, map2) -> {};
            try {
                Map<LocalDate, Multiset<String>> map = Files.lines(path)
                                                            .collect(supplier, accumulator, combiner);
                Multiset<String> value = map.get(search);
                if (value != null) {
                    Optional<String> most = value.elementSet()
                                                 .stream()
                                                 .max((s1, s2) -> value.count(s1) - value.count(s2));
                    most.ifPresentOrElse(System.out::println,
                                         () -> System.out.println("No max found."));
                }
                else {
                    System.out.println("No value for: " + search);
                }
            }
            catch (IOException xIo) {
                xIo.printStackTrace();
            }
        }
    }
    
    1. 我使用了任意搜索关键字。您可以通过任何方式获取搜索键,例如接受用户的值。
    2. 我从 CSV 文件中的行创建了一个Map。 Map 键是 CSV 文件中每一行的日期字段,Map 值是 Multiset,其中包含每个元素一次,但还包含每个元素添加到 Multiset 的次数的计数.
    3. 读取整个文件后,我得到了与搜索键关联的Map 值。
    4. 如果Map 中有搜索键条目,我会从与搜索键关联的Multiset 中获取计数最高的元素。
    5. 我打印出在搜索日期中点击率最高的 URL。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多