【问题标题】:sorting based on key from a file基于文件中的键排序
【发布时间】:2013-09-20 03:31:17
【问题描述】:

我需要对从类似以下的文件中读取的内容进行排序:

Key: 2 rec:1 2 3 4 5 6 ...
Key: 3 rec:7 8 9 10 11 ...
Key: 1 rec:A B C D E F ...

变成

Key: 1 rec:A B C D E F ...
Key: 2 rec:1 2 3 4 5 6 ...
Key: 3 rec:7 8 9 10 11 ...

如果我们在 key 的 rec (record ) 中有未排序的内容,它将保持不变!因为排序是基于键的。我想使用 C 中定义的 qsort() 进行排序。我有一个想法,使用 strtok 将从文件中读取的每一行分解为可管理的数组,但如果它是找到键号以便使用 C 库中的 qsort 对它们进行排序的最佳方法,我不是。

P.S.:输入文件的每一行都包含一个键,例如 Key: 1 rec:A B C D E F ... 此外,我们不会对键中的记录进行排序。

【问题讨论】:

  • 我建议查看 unix 中的sort 命令。你的问题有点不清楚,但sort -n yourfile > sortedfile 可能会做你想做的事(对文件进行数字排序)
  • 关键部分的长度是否相等?如果是这样,您可以编写一个比较函数,该函数仅根据 strncmp() 对每一行的头部进行排序(并使用指针魔法跳过“键:”)。您是否试图避免将所有行加载到内存中?
  • @n0741337 我不一定这么认为。这是排序程序paste.ubuntu.com/6113022 的输入文件的一个示例!还有什么其他方法可以用来提取key的值并根据它的值对文件的每一行进行排序?
  • 然后查看sort -k2,2 -n。否则,因为它们不是固定长度的部分,您将需要自己识别前两个空格。为此,您可以在比较函数中使用指针而不是 strtok()。
  • 如果您的工作是对数据进行排序,请使用sort -k2,2nsort -k2,2 -n,然后您就回家了。如果您的工作是编写 C 代码,那么您还有一些工作要做......数据集是否足够小以适合内存,或者您是否必须担心外部排序(读取尽可能多的数据以适合内存,对其进行排序,将其写入临时文件,重复直到读取所有数据,然后合并所有文件)?如果你能把它全部放在记忆中,那就不会太痛苦了。最大的数字适合 32 位 int(一个有符号的,除非我误读了数据)。 [...继续...]

标签: c file unix sorting


【解决方案1】:

如果你必须写 C,它不需要那么长或那么复杂。如果您忽略错误检查,您可以简化它。

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern void err_exit(const char *fmt, ...);

typedef struct data
{
    char *line;
    int   key;
} data;

static int cmp_data(const void *v1, const void *v2)
{
    const data *d1 = v1;
    const data *d2 = v2;
    if (d1->key < d2->key)
        return -1;
    else if (d1->key > d2->key)
        return +1;
    else
        return 0;
}

int main(void)
{
    char buffer[4096];
    data *array = 0;
    size_t array_len = 0;
    size_t array_max = 0;

    while (fgets(buffer, sizeof(buffer), stdin) != 0)
    {
        if (array_len >= array_max)
        {
            size_t new_size = (array_max + 2) * 2;
            void *space = realloc(array, new_size * sizeof(data));
            if (space == 0)
                err_exit("Out of memory (1)");
            array = space;
            array_max = new_size;
        }
        array[array_len].line = strdup(buffer);
        if (array[array_len].line == 0)
            err_exit("Out of memory (2)");
        if (sscanf(array[array_len].line, "%*s %d", &array[array_len].key) != 1)
            err_exit("Format error - no number in right place in: %.20s...\n",
                     array[array_len].line);
        //printf("%3zu:%.10d: %s", array_len, array[array_len].key,
        //       array[array_len].line);
        array_len++;
    }

    qsort(array, array_len, sizeof(data), cmp_data);

    for (size_t i = 0; i < array_len; i++)
        fputs(array[i].line, stdout);

    return 0;
}

void err_exit(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    putc('\n', stderr);
    exit(EXIT_FAILURE);
}

keysort — 在文件排序时覆盖文件

#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void sort_file(const char *i_file, const char *o_file);

int main(int argc, char **argv)
{
    if (argc > 1)
    {
        for (int i = 1; i < argc; i++)
            sort_file(argv[i], argv[i]);
    }
    else
        sort_file("/dev/stdin", "/dev/stdout");
    return 0;
}

typedef struct data
{
    char *line;
    int   key;
} data;

static int cmp_data(const void *v1, const void *v2)
{
    const data *d1 = v1;
    const data *d2 = v2;
    if (d1->key < d2->key)
        return -1;
    else if (d1->key > d2->key)
        return +1;
    else
        return 0;
}

static void err_exit(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    putc('\n', stderr);
    exit(EXIT_FAILURE);
}

void sort_file(const char *i_file, const char *o_file)
{
    char buffer[4096];
    data *array = 0;
    size_t array_len = 0;
    size_t array_max = 0;

    FILE *i_fp = fopen(i_file, "r");
    if (i_fp == 0)
        err_exit("Failed to open file %s for reading", i_file);

    while (fgets(buffer, sizeof(buffer), i_fp) != 0)
    {
        if (array_len >= array_max)
        {
            size_t new_size = (array_max + 2) * 2;
            void *space = realloc(array, new_size * sizeof(data));
            if (space == 0)
                err_exit("Out of memory (1)");
            array = space;
            array_max = new_size;
        }
        array[array_len].line = strdup(buffer);
        if (array[array_len].line == 0)
            err_exit("Out of memory (2)");
        if (sscanf(array[array_len].line, "%*s %d", &array[array_len].key) != 1)
            err_exit("Format error - no number in right place in: %.20s...\n",
                     array[array_len].line);
        //printf("%3zu:%.10d: %s", array_len, array[array_len].key,
        //       array[array_len].line);
        array_len++;
    }
    fclose(i_fp);

    qsort(array, array_len, sizeof(data), cmp_data);

    FILE *o_fp = fopen(o_file, "w");
    if (o_fp == 0)
        err_exit("Failed to open file %s for writing", o_file);
    for (size_t i = 0; i < array_len; i++)
        fputs(array[i].line, o_fp);
    fclose(o_fp);
}

如果你的系统不支持/dev/stdin/dev/stdout,那么你得修改接口为sort_file(),大概是:

void sort_file(const char *i_file, FILE *ifp, const char *o_file, FILE *ofp);

然后您决定如果ifp 不为空,则将其用于输入——否则您将打开i_file 指定的文件。类似的输出:如果ofp 不为空,则使用它——否则,打开o_file 指定的文件。对main()sort_file() 正文的更改是微不足道的。

【讨论】:

  • 在上面的代码中你在哪里处理 FILE 指针和文件名?这种排序需要从文件中读取数据,然后进行一些中间计算,然后再次写回文件。如果您知道某个版本(或可以修改您的版本)以处理 C 中的文件的方式,请告诉我。谢谢。
  • 代码从stdin 读取(参见fgets() 行)并写入stdout(参见fputs() 行)。如果您想从文件中读取,您必须安排将代码包装在一个合适的循环中,该循环打开命名文件,读取它们,然后关闭它们。如果您想覆盖原始输入文件(您确定吗?),那么您将打开文件进行写入(然后写入,然后关闭它)。例行更改...不是很难(可能需要 10 分钟的时间来编写代码,并进行一些测试)。主要需要一些额外的数据来处理——多次测试一个文件......不是很丰富。
  • @JonathanLeffler,为什么不使用freopen?这样,您只需要使用 printf 和 scanf 而不必担心所有那些 FILE* 指针对象
  • @Smac89:我从来没有打扰过;我不喜欢丢失标准输入和标准输出,并且编写代码以使用给定的文件指针对我来说很正常。各有各的。我想它会起作用(我看不出它不起作用的原因),但这不是我曾经做过的方式,我不确定我现在会改变。
【解决方案2】:

要在 c 中执行此操作,请使用 sscanf,您可以获得一种正则表达式来提取所需的整数:

int comp(const void *str1, const void *str2) {
    char *a = *(char **)str1, *b = *(char **)str2;
    int key1, key2;
    sscanf(a, "%*s%d", &key1);
    sscanf(b, "%*s%d", &key2);
    return key1-key2;
}

//Call the function qsort like so
qsort(/*char **/lines, /*int*/numElements, /*unsigned*/ sizeof (char*), comp);

不知道如何在 c++ 中使用 regex 库,但 sscanf 仍然有效。 c++11 中的完整工作示例:

#include <iostream>
#include <cstdio>
#include <deque>
#include <string>
#include <algorithm>

int main() {

    //Using fstream, read in each line of the file into a string using getline(...)
    std::deque<std::string> lines = {
        "Key: 2 rec:1 2 3 4 5 6",
        "Key: 3 rec:7 8 9 10 11",
        "Key: 1 rec:A B C D E F",
        "Key: 4 rec:1 2 3 4 5 6"
    }; //Store each in a deque object

    //using std::sort
    std::sort(lines.begin(), lines.end(), []( const std::string &str1, const std::string &str2 ) {
        int key1, key2;
        sscanf(str1.c_str(), "%*s%d", &key1);
        sscanf(str2.c_str(), "%*s%d", &key2);
        return (key1 < key2);
    });


    for (auto sortedkeys: lines)
        std::cout << sortedkeys << "\n";
    return 0;
}

【讨论】:

  • 这个程序在 C 中的等价物是什么?我不应该使用 C++。我也知道如何使用 qsort 以及如何在 C 中编写比较函数。我主要需要知道如何在 key: 之后精确地获取数字:以及如何根据键对所有行进行排序。非常感谢。
  • 我已经向你展示了 compare 函数的样子,所以你现在要做的就是用你正在读取的文件中的所有行填充一个数组,并计算行读;然后使用我展示的方式调用 qsort 。应该很容易
【解决方案3】:

如果密钥长度不同,则应避免使用 strncmp 并逐行读取,然后使用从行 [5] 到下一个空格的循环获取密钥值(或者使用带有空格分隔符的 strtok )。

重复这个直到EOF。将键值存储在数组或列表中。

下一个排序数组或列表。

现在使用strstr 从文件中的排序数组中找到 Key 的值,并将匹配的行复制到新文件中。在使用 strstr 之前将键转换为字符串。

如果您想避免处理新文件,需要使用fseek 和修改行在行之间移动文件指针。

【讨论】:

  • 我同意你的回答,我们应该找到第 [5] 行,因为在“key: 1804289383”中,180...从第 [5] 行开始。如何从文件中取出行并将它们放入字符串?我们在 C 中有任何这样的 get_line 预定义函数吗?另外我认为如果我使用 line[5] 方法,我基本上不需要使用 strtok,对吧?
  • 定义 MAX_LINE。 #定义MAX_LINE maximum length of line+1。声明一个字符串char line[MAX_LINE]。首先在fopen()的帮助下以r+模式打开文件例如:fp=fopen("file","r+"),然后使用fgets()例如:fgets(line,MAX_LINE,fp)从文件中读取该行。使用字符串线。如果使用 line[5] 到下一个空格,则不需要使用 strtok。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-14
  • 1970-01-01
  • 2022-08-09
  • 1970-01-01
  • 1970-01-01
  • 2012-10-05
相关资源
最近更新 更多