V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
1ven
V2EX  ›  程序员

如何实现检查一个大 txt 文件里数据是否重复的功能

  •  
  •   1ven · 2024-02-27 19:28:32 +08:00 · 5041 次点击
    这是一个创建于 369 天前的主题,其中的信息可能已经有所发展或是发生改变。

    如何实现检查一个大 txt 文件里数据是否重复的功能

    如题,文件的每一行数据结构都是相同的,字段值通过一个自定义分隔符分割。比如有如下结构数据,id|name|bizNo ,检查 id 是否重复。

    最好是 java 实现的
    49 条回复    2024-02-29 23:14:33 +08:00
    GeruzoniAnsasu
        1
    GeruzoniAnsasu  
       2024-02-27 19:33:10 +08:00
    你们 java 八股不是很喜欢考 hashmap 来着
    Rickkkkkkk
        2
    Rickkkkkkk  
       2024-02-27 19:34:26 +08:00   ❤️ 1
    @GeruzoniAnsasu 八股文考的一般是内存里存不下, 比如文件是 1T 大小
    HojiOShi
        3
    HojiOShi  
       2024-02-27 19:45:00 +08:00
    要检查一个包含重复数据的文本文件中的数据是否重复,你可以按照以下步骤进行:

    使用 BufferedReader 和文件流读取文件内容。
    将每行内容分割为字段,这里使用你提供的自定义分隔符 | 作为字段间分隔符。
    使用哈希表(或 HashSet )来存储每个字段的值。
    遍历每个字段的值,如果已经在哈希表中存在该值,则说明数据重复,否则将该值添加到哈希表中。
    检查哈希表的大小,如果大小大于预期 Repeat Count ,则说明数据存在重复。
    下面是一个 Java 示例代码,你可以根据自己的需求进行修改:

    import java.io.*;
    import java.util.HashMap;
    import java.util.Map;

    public class CheckDuplicateData {
    public static void main(String[] args) throws IOException {
    String fileName = "data.txt"; // 文件名
    String delimiter = "|"; // 自定义分隔符
    int repeatCount = 2; // 预期的重复次数

    Map<String, Integer> countMap = new HashMap<>(); // 统计每个字段的值出现的次数

    try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
    String line;
    while ((line = br.readLine()) != null) {
    String[] fields = line.split(delimiter); // 分割每行为字段
    for (String field : fields) {
    if (!countMap.containsKey(field)) {
    countMap.put(field, 0);
    }
    countMap.put(field, countMap.get(field) + 1);
    }
    if (countMap.size() > repeatCount) {
    System.out.println("数据存在重复");
    break;
    }
    }
    }
    }
    }

    在上面的示例代码中,我们使用了 HashMap 来统计每个字段的值出现的次数。在遍历文件内容时,对于每个字段,我们首先检查是否已经存在于哈希表中,如果不存在,则将其添加到哈希表中并记录计数;如果已经存在于哈希表中,则将计数加一。最后,检查哈希表的大小是否大于预期重复次数,如果是,则输出提示信息并退出循环。
    mubai
        4
    mubai  
       2024-02-27 19:50:44 +08:00
    遍历大 txt 并按照 id 取模拆分成多个小 txt ,读取小 txt 在内存中判断重复。
    zihuyishi
        5
    zihuyishi  
       2024-02-27 19:58:38 +08:00
    印象中这种问题是分组吧,按照首字母或者什么方式 hash 一下分成 n 个小文件,然后再小文件内依次检查是否有重复的
    maoyikun
        6
    maoyikun  
       2024-02-27 20:00:37 +08:00
    Java 的 NIO 随机读取文件 RandomAccessFile 应该可以满足需求
    Jirajine
        7
    Jirajine  
       2024-02-27 20:11:01 +08:00   ❤️ 1
    用 txt 存储大体量的数据是很少见的场景,只在一些传播的裤子中见到过。
    正常来说需要对这种数据进行清洗分析之前最好先导入到正经的数据库里。
    tutudou
        8
    tutudou  
       2024-02-27 20:30:24 +08:00
    这个我写过类似的,是大文本分割,无论文本多大不经过内存,通过文件指针的方式直接分割文本。你这个要看具体是多大了,看把所有 ID 加入到内存能不能放的下,如果能放得下直接排序不就得了简单省事。如果放不下,考虑要不炫技使用文件指针的骚方式解决。要不把所有数据存入数据库,把 ID 设为唯一且不重复。
    yuruizhe
        9
    yuruizhe  
       2024-02-27 21:41:02 +08:00
    @Rickkkkkkk 如果 id 是整数,用一个布尔数组呗,1T 比特内存,切成若干分页文件
    icedx
        10
    icedx  
       2024-02-27 21:52:51 +08:00
    前年用 rust 写过一个, 卖了 2K XD
    NESeeker
        11
    NESeeker  
       2024-02-28 01:13:19 +08:00 via Android   ❤️ 1
    裤子?
    xarthur
        12
    xarthur  
       2024-02-28 01:34:37 +08:00 via iPhone
    既然是大文件,首先第一个问题就是多大……
    levelworm
        13
    levelworm  
       2024-02-28 03:10:48 +08:00 via Android
    数据库行吗?
    nutting
        14
    nutting  
       2024-02-28 08:41:58 +08:00   ❤️ 1
    这是需要完美解决的算法题还是一般实际工程问题?
    netnr
        15
    netnr  
       2024-02-28 08:57:10 +08:00 via Android
    布隆过滤器

    如果结果不准确,增强或多个 Hash 计算
    dode
        16
    dode  
       2024-02-28 09:06:57 +08:00
    先用 HashSet 存看看,不够再说
    miniliuke
        17
    miniliuke  
       2024-02-28 09:07:59 +08:00
    直接布个 spark 集群,文件大就加机器......
    gitdoit
        18
    gitdoit  
       2024-02-28 09:13:36 +08:00
    简单点 用 SQLite
    silencil
        19
    silencil  
       2024-02-28 09:22:01 +08:00
    这没说到底多大的文件啊,简单点 map 计数都行,实在大的离谱不得搭个大数据框架 mapreduce 下:) doge
    bookerlevit
        20
    bookerlevit  
       2024-02-28 09:27:19 +08:00
    有一个软件叫“大数据去重复工具”,可以去重复,而且超大文件也可以,他是分段去去重复,最后再整合的。
    layxy
        21
    layxy  
       2024-02-28 09:29:23 +08:00
    使用 BufferedReader 逐行读取,如果只需要排重使用 set 就可以,如果需要重复计数排序可以使用 map
    guanzhangzhang
        22
    guanzhangzhang  
       2024-02-28 09:36:19 +08:00
    @icedx #10 2K XD 是啥意思
    o562dsRcFqYl375i
        23
    o562dsRcFqYl375i  
       2024-02-28 09:39:34 +08:00
    要不咋们跳出单机器的思维,spark 一下?
    o562dsRcFqYl375i
        24
    o562dsRcFqYl375i  
       2024-02-28 09:41:25 +08:00
    @nutting 还是老哥想的周全
    Greenm
        25
    Greenm  
       2024-02-28 09:43:21 +08:00
    经常接触过这样的需求,如果不考虑一定要原始文件中字符的顺序,我的选择是 Linux 上的 sort | uniq ,流式处理速度非常快。
    FreeEx
        26
    FreeEx  
       2024-02-28 09:50:47 +08:00   ❤️ 2
    1ven
        27
    1ven  
    OP
       2024-02-28 10:12:57 +08:00
    @FreeEx 上次我也看到过有人发过这个链接,就是找不到了,老哥知道原贴吗
    815979670
        28
    815979670  
       2024-02-28 10:27:47 +08:00
    duckDB 读入 用 SQL 查询出来?
    Subfire
        29
    Subfire  
       2024-02-28 10:51:33 +08:00
    用正则匹配重复行即可:
    ^(.+?)$(?=.*?^\1$)
    fuis
        30
    fuis  
       2024-02-28 11:06:18 +08:00
    一行脚本可解

    ```
    [ $(cat 1.txt | wc -l) = $(cat 1.txt | awk -F, '{print $1}' | sort -n | uniq | wc -l) ]; echo $?
    ```
    beyondstars
        31
    beyondstars  
       2024-02-28 11:15:23 +08:00
    可以用 trie tree (也叫前缀树)来实现,将文本文件打开为一个 stream, 把这个 stream pipe 到一个 trie tree 型对象里面,每一个 insert 之后返回对应节点的指针,然后检查该节点的 count 是否大于 1.
    beyondstars
        32
    beyondstars  
       2024-02-28 11:22:12 +08:00
    伪代码如下:

    let trieTree = new TrieTree();
    let lineStream = openStream("data.txt");
    while lineStream is not EOF:
    let line = lineStream.getLine();
    let field1Value = split(line)[0];
    let lastNode = trieTree.insert(field1Value);
    if (lastNode.count > 1)
    print("Repeated.")
    return
    beyondstars
        33
    beyondstars  
       2024-02-28 11:24:01 +08:00
    不过 trieTree 的 insert 操作一般接受的是一个 path ,你把 string 转换成 char[] 再 insert 到 trieTree ,这样 string 也可以看作是 path 。
    wangtian2020
        34
    wangtian2020  
       2024-02-28 11:39:06 +08:00
    如果不爆内存,我的做法是

    nodejs 读取文件为字符串
    换行符/t 分隔字符串为数组
    数据把 id map 出来
    对数组进行 [...new Set] 变换
    判断变换前后 length 是否一致
    ffw5b7
        35
    ffw5b7  
       2024-02-28 11:44:06 +08:00 via Android
    类似分库分表怎么排序查询 top10 一样处理
    Tiking
        36
    Tiking  
       2024-02-28 11:49:56 +08:00
    老实交代,前几天的上亿裤子,你是不是下完了
    nuII
        37
    nuII  
       2024-02-28 14:41:29 +08:00
    @NESeeker 脱裤,就是数据库
    sud0day
        38
    sud0day  
       2024-02-28 15:19:26 +08:00
    @Jirajine 加一,我一听描述感觉就是 TG 上的社工库
    sankooc
        39
    sankooc  
       2024-02-28 15:34:34 +08:00
    准确率要求高么 不高的话可以考虑 HLL
    FaiChou
        40
    FaiChou  
       2024-02-28 15:49:48 +08:00   ❤️ 1
    @beyondstars #31 我想到的也是 Trie 数据结构
    hemingwang0902
        41
    hemingwang0902  
       2024-02-28 16:17:54 +08:00
    @HojiOShi chatGPT 用得很溜
    xxiu
        42
    xxiu  
       2024-02-28 16:45:04 +08:00
    id 如果是数字的话,使用位来表示,1G 可以表示 8589934592 个位置。 只需要一个循环。
    xiaohundun
        43
    xiaohundun  
       2024-02-28 16:58:43 +08:00
    我比较好奇楼上提到的分段去重怎么做到呢
    一段内不重复不代表整个文件不重复,请教下
    chtcrack
        44
    chtcrack  
       2024-02-28 17:04:51 +08:00
    在 Java 中,检查一个大文本文件中数据是否重复可以通过多种方法实现。以下是一种简单的实现方式:
    读取文件中的每一行。
    解析每行数据,提取出 id 字段。
    将提取出的 id 存储到某种数据结构中,例如 HashSet ,以快速检查重复。
    遍历文件,同时检查新读取的 id 是否已经在数据结构中。
    以下是一个简单的 Java 代码示例,演示了如何实现这一功能:
    import java.io.BufferedReader;
    import java.io.FileReader;
    import java.io.IOException;
    import java.util.HashSet;

    public class CheckDuplicate {
    public static void main(String[] args) {
    String filePath = "path/to/your/file.txt"; // 替换为你的文件路径
    String separator = "|"; // 数据字段之间的分隔符

    HashSet<String> ids = new HashSet<>();
    boolean hasDuplicates = false;

    try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
    String line;
    while ((line = br.readLine()) != null) {
    String[] fields = line.split(separator);
    String id = fields[0]; // 假设 id 在第一个字段

    if (ids.contains(id)) {
    System.out.println("Duplicate ID found: " + id);
    hasDuplicates = true;
    } else {
    ids.add(id);
    }
    }
    } catch (IOException e) {
    e.printStackTrace();
    }

    if (!hasDuplicates) {
    System.out.println("No duplicates found.");
    }
    }
    }
    在这个代码示例中,我们使用了 HashSet 来存储所有唯一的 id ,因为 HashSet 提供了 O(1)的时间复杂度来检查一个元素是否存在于集合中。这使得检查重复变得非常高效。
    请注意,这个代码示例没有进行详细的错误处理,实际应用中可能需要添加更多的异常处理和资源管理代码。此外,根据您的文件大小和内存限制,可能需要考虑使用更高效的数据结构或读取文件的部分内容以避免内存溢出。
    tyrone2333
        45
    tyrone2333  
       2024-02-28 17:05:11 +08:00
    @guanzhangzhang #22 2K 金额 XD 笑
    Akiya
        46
    Akiya  
       2024-02-29 10:55:14 +08:00
    没爆内存就 set 存,爆内存就放数据库查
    lslqtz
        47
    lslqtz  
       2024-02-29 18:17:42 +08:00
    实现一个 func, 以一定的 buffer 顺序读文件, buffer 长度要大于最长匹配词长度的 2 倍, 做匹配, 若匹配到存 offset 或直接返回 bool.
    lslqtz
        48
    lslqtz  
       2024-02-29 18:19:42 +08:00
    哦, 我以为是匹配给定 keyword 呢, 匹配规律的小数据的话也可以从头读到尾提出来匹配.
    Rorysky
        49
    Rorysky  
       2024-02-29 23:14:33 +08:00
    sort *.txt | uniq -d
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3831 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 202ms · UTC 10:23 · PVG 18:23 · LAX 02:23 · JFK 05:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.