AI大法之C语言哈希表算法比较两个文件去重

最近朋友在工作上遇到了一个问题,经常需要比对两个文件,筛选出文件中不同的订单号。比如有两个文件:计费.txt受理.txt,文件中每一行都是一个订单号,需要找出计费.txt文件中有而受理.txt文件中没有的单号和计费.txt文件中没有而受理.txt文件中有的单号。

虽然有点绕,但仔细一看不就是简单的去重问题吗。于是我就说,这都2024年了,你把这两个文件上传给豆包文心一言kimi这些AI模型,直接让AI帮你找出来不就完事了么。结果并没有想象的那么顺利,由于文件行数不确定,大致在1W-20W行不等,上传给AI时,只给你加载一半数据,也不知道是不是没开会员的原因,想白嫖AI这条路算是行不通了。

于是我提出,干脆写个shell脚本去做这个操作呗。但朋友嫌弃shell脚本太慢了,想让我用C语言帮忙写一个,并请我喝奶茶。我心想这事情简单,有奶茶喝我就给你干了。

刚开始思路是,打开一个文件读一行,跟另一个文件每一行比,比完发现没有重复就存到一个新文件。后面一想,这样做文件行数一多,效率肯定极慢。于是问了问豆包,有什么好办法,豆包给出了个哈希表算法的建议。当了这么久牛马了,现在有了AI这个牛马,我肯定是不会自己从零开始撸代码的了,先让AI帮我写一段。

我copy过来,果然不出所料,编译都编不过。返回来质问AI,AI又老老实实给我整了一段,我找朋友要了两个测试数据,一测试发现不行,然后又让它改,然后给他提了一系列要求、比如哈希碰撞处理什么的,就这么一步步在我的调教下,终于写出了一段能用的代码。大家如果用得上可自取:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // 用于isspace函数

#define MAX_LINE_LENGTH 1024
#define HASH_TABLE_SIZE 100000

typedef struct Node {
    char *singleNumber;
    struct Node *next;
} Node;

Node *hashTable[HASH_TABLE_SIZE];

// 函数声明
unsigned int hashFunction(const char *str);
void insertHash(Node **hashTable, const char *singleNumber);
int containsHash(Node **hashTable, const char *singleNumber);
void freeHashTable(Node *hashTable[]);
int isNotBlankLine(const char *line);
char* trim(const char *str);

int generaFile(const char *filename1, const char *filename2)
{
    FILE *file1, *file2, *outputfd;
    char output_filename[1024] = {0};
    strncpy(output_filename, filename2, strlen(filename2) - 4);
    strcat(output_filename, "_去重后.txt");

    char line[MAX_LINE_LENGTH];
    // 初始化哈希表
    for (int i = 0; i < HASH_TABLE_SIZE; i++) {
        hashTable[i] = NULL;
    }
    // 读取第一个文件并构建哈希表
    file1 = fopen(filename1, "r");
    if (!file1) {
        perror("Error opening file1");
        return EXIT_FAILURE;
    }
    while (fgets(line, MAX_LINE_LENGTH, file1)) {
        char *trimmedLine = trim(line);
        if (isNotBlankLine(trimmedLine)) {
            insertHash(hashTable, trimmedLine);
        }
        free(trimmedLine);
    }
    fclose(file1);
    printf("load %s hash finish!\n", filename1);

    // 读取第二个文件并删除共同的单号
    file2 = fopen(filename2, "r");
    outputfd = fopen(output_filename, "w+");
    if (!file2 || !outputfd) {
        perror("Error opening file2 or outputfd");
        return EXIT_FAILURE;
    }

    while (fgets(line, MAX_LINE_LENGTH, file2)) {
        char *trimmedLine = trim(line);
        line[strcspn(line, "\n")] = 0; // 移除换行符
        if (isNotBlankLine(trimmedLine) && !containsHash(hashTable, trimmedLine)) { // 没有找到相同的且不是空行
            fputs(trimmedLine, outputfd);
            fputc('\n', outputfd); // 使用fputc确保只写入一个换行符
        }
        free(trimmedLine);
    }
    fclose(file2);
    fclose(outputfd);

    // 释放哈希表
    freeHashTable(hashTable);

    return EXIT_SUCCESS;
}

int isNotBlankLine(const char *line) {
    // 检查每一字符是否都是空白字符,如果是,则返回0,否则返回1
    while (*line) {
        if (!isspace((unsigned char)*line)) {
            return 1;
        }
        line++;
    }
    return 0;
}

char* trim(const char *str) {
    if (str == NULL) return NULL;
    const char *end;
    size_t len = strlen(str);
    // Trim leading space
    while (isspace((unsigned char)*str)) str++;
    if (*str == 0)  // All spaces?
        return strdup("");
    // Trim trailing space
    end = str + len - 1;
    while (end > str && isspace((unsigned char)*end)) end--;
    // The len is the number of non-space chars
    len = (end - str + 1);
    char *trimmed = (char *)malloc(len + 1);
    if (trimmed) {
        snprintf(trimmed, len + 1, "%s", str);
        trimmed[len] = '\0';
    }
    return trimmed;
}

int main(int argc, char *argv[])
{
    char *filename1 = argv[1];
    char *filename2 = argv[2];
    if (argc != 3) {
        printf("请指定要去重的两个文件!\n");
        printf("用法: ./quchong 文件1 文件2\n");
        printf("示例:./quchong 受理.txt 计费.txt\n");
        return 0;
    }

    if (generaFile(filename1, filename2) != EXIT_SUCCESS) {
        printf("%s 去重失败!", filename2);
        return EXIT_FAILURE;
    }

    // 注意:这里应该重新初始化哈希表
    for (int i = 0; i < HASH_TABLE_SIZE; i++) {
        hashTable[i] = NULL;
    }
    if (generaFile(filename2, filename1) != EXIT_SUCCESS) {
        printf("%s 去重失败!", filename1);
        return EXIT_FAILURE;
    }

    printf("数据去重完成!\n");

    return EXIT_SUCCESS;
}

// 哈希函数
unsigned int hashFunction(const char *str)
{
    unsigned int hash = 5381;
    int c;
    while ((c = *str++)) {
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
    }
    return hash % HASH_TABLE_SIZE;
}

// 插入哈希表
void insertHash(Node **hashTable, const char *singleNumber)
{
    unsigned int index = hashFunction(singleNumber);
    Node *newNode = (Node *)malloc(sizeof(Node));
    if (!newNode) {
        perror("Memory allocation failed");
        exit(EXIT_FAILURE);
    }
    newNode->singleNumber = strdup(singleNumber);
    if (!newNode->singleNumber) {
        free(newNode);
        perror("Memory allocation failed");
        exit(EXIT_FAILURE);
    }
    newNode->next = hashTable[index];
    hashTable[index] = newNode;
}

// 检查哈希表中是否包含某个单号
int containsHash(Node **hashTable, const char *singleNumber)
{
    unsigned int index = hashFunction(singleNumber);
    Node *current = hashTable[index];
    while (current) {
        if (strcmp(current->singleNumber, singleNumber) == 0) {
            return 1;
        }
        current = current->next;
    }
    return 0;
}

// 释放哈希表内存
void freeHashTable(Node *hashTable[])
{
    for (int i = 0; i < HASH_TABLE_SIZE; i++) {
        Node *current = hashTable[i];
        while (current) {
            Node *temp = current;
            current = current->next;
            free(temp->singleNumber);
            free(temp);
        }
    }
}

不得不感叹现在AI的强大啊,就几轮对话的功夫就给我写出了一个基本能用的代码,大大提高了我们的工作效率。

相关推荐
加载中loading...17 分钟前
Linux线程安全(二)条件变量实现线程同步
linux·运维·服务器·c语言·1024程序员节
Wx120不知道取啥名21 分钟前
C语言之长整型有符号数与短整型有符号数转换
c语言·开发语言·单片机·mcu·算法·1024程序员节
biomooc1 小时前
R语言 | paletteer包:拥有2100多个调色板!
r语言·数据可视化·1024程序员节
Hello.Reader1 小时前
FFmpeg 深度教程音视频处理的终极工具
ffmpeg·1024程序员节
Y.O.U..2 小时前
STL学习-容器适配器
开发语言·c++·学习·stl·1024程序员节
就爱敲代码2 小时前
怎么理解ES6 Proxy
1024程序员节
憧憬一下2 小时前
input子系统的框架和重要数据结构详解
arm开发·嵌入式·c/c++·1024程序员节·linux驱动开发
三日看尽长安花3 小时前
【Tableau】
1024程序员节
sswithyou3 小时前
Linux的调度算法
1024程序员节
武子康3 小时前
大数据-187 Elasticsearch - ELK 家族 Logstash Filter 插件 使用详解
大数据·数据结构·elk·elasticsearch·搜索引擎·全文检索·1024程序员节