最近朋友在工作上遇到了一个问题,经常需要比对两个文件,筛选出文件中不同的订单号。比如有两个文件:计费.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的强大啊,就几轮对话的功夫就给我写出了一个基本能用的代码,大大提高了我们的工作效率。