Чтение текстового файла и сортировка двух массивов

У меня есть «Temp.dat» с 200 строками, который выглядит так:

0.060493 1 0.5 1
1.596961 0 0.1 2
0.87758  1 0.3 1.5
0.165453 1 0   3
0.07085  1 0.3 4
0.125379 1 0.2 3
0.454202 1 0.2 2
0.373227 1 0.3 1
0.131486 1 0.3 3
0.867477 0 0.5 4
0.122609 0 0.8 9

Я пытаюсь прочитать каждую строку и сохранить каждый столбец данных в отдельные массивы со следующим кодом:

#include <stdio.h>
#include <stdlib.h>
#define MAX 200

int main(){

  double x[MAX], y[MAX];
  double a, b;
  int m = 0;
  FILE* file; 
  file = fopen("Temp.dat", "r");
  if ( file != NULL ){
      /* Read the two columns. */
      while(fscanf(file, "%d %d", &a, &b) == 2)
      { x[m] = a;
        y[m] = b;
        m++;
      }
  }
  printf("%d %d\n", x[4], y[1]); # Test the stored inputs of the two arrays
  return(0);
}    

Когда я попытался распечатать результат, он дал 1 13258992, а не 0.165453 0. Я не могу понять, откуда взялась пара 1 13258992, так как я думал, что строка fscanf(file, "%d %d", &a, &b) == 2 делала то, что должна была делать: просматривала каждую строку файла Temp.dat и считывала два целых числа double-type, которые затем сохранялись в двух массивах x[MAX] и `y[MAX ]. Поэтому, может ли кто-нибудь помочь мне решить эту проблему?

Другой вопрос: после сохранения двух столбцов выше в двух массивах x[MAX] и y[MAX] я хотел бы отсортировать два массива в порядке возрастания на основе значений в первом массиве x[MAX]. Это будет выглядеть так:

    0.060493 1
    0.07085  1
    0.122609 0
    0.125379 1
    0.131486 1
    0.165453 1
    0.373227 1
    0.454202 1
    0.867477 0

Как я мог сделать эту процедуру sorting в C, поскольку довольно сложно расположить элементы в y[MAX] так, чтобы они следовали порядку соответствующих им элементов в x[MAX]?


person user177196    schedule 19.02.2018    source источник
comment
@Pablo: Спасибо за помощь. Я добавил условие в while и изменил %d на %lf, но результат все равно неверный. Теперь он дал мне -1 34902256 за те же x[4] y[1]. Не могли бы вы дать мне несколько советов о том, как сортировать второй массив, пока я меняю местами каждый элемент первого массива?   -  person user177196    schedule 19.02.2018
comment
Я удалил свой комментарий и разместил его как ответ. У вас была такая же ошибка в вызове printf. Смотрите мой ответ.   -  person Pablo    schedule 19.02.2018


Ответы (3)


Вы можете объявить структуру с x и y, чтобы два значения были связаны вместе, x и y сохраняли свою связь, когда элементы массива меняются местами во время сортировки. Пример:

struct data_t
{
    double x;
    int y;
};

int compare(const void *a, const void *b)
{
    const struct data_t *m = a;
    const struct data_t *n = b;
    if(m->x == n->x)
        return m->y > n->y;
    return m->x > n->x;
}

int main(void) 
{
    struct data_t data[MAX];
    FILE* file = fopen("Temp.dat", "r");
    int count = 0;
    while(fscanf(file, "%lf %d", &data[count].x, &data[count].y) == 2)
    {
        count++;
        if (count == MAX)
            break;
    }

    qsort(data, count, sizeof(struct data_t), compare);
    for(int i = 0; i < count; i++)
        printf("%lf %d\n", data[i].x, data[i].y);
    return 0;
}

Используйте %lf для значений double или используйте %f для float , как показано в других ответах

Что касается функции сравнения, скажем, у вас есть эти значения в ваших данных:

0.060493 1
0.060493 5
0.060493 2

В этом случае значение x из первого элемента совпадает со значением x из другого элемента. Если вы проверяете только m->x > n->x, сортировка не происходит. Итак, вы хотите сравнить m->y > n->y, если x одно и то же.

qsort — стандартная функция C. Он не знает вашего типа данных, мы должны сообщить ему о типе данных. Это делается внутри функции сравнения, которая получает указатели a и b, мы знаем, что это указатели на элементы в массиве data, так что это причина преобразования.

Набор данных может содержать до 200 элементов, поскольку мы объявили его как struct data_t data[MAX];, где MAX равно 200.


Редактировать 3 ******
Работая с массивами, объявить массив:

double arr[MAX + 1][8]

Обратите внимание, что первый индекс массива — это строки с 0 по MAX. Он настроен таким образом, чтобы его можно было отсортировать позже. Теперь мы можем прочитать файл непосредственно в массив и отсортировать массив (нет необходимости в структуре). Убедитесь, что вы не перепутали это со старыми решениями, которые имели arr[8][MAX + 1]

int compare_2d_array(const void *pa, const void *pb)
{
    double a = *(double*)pa;
    double b = *(double*)pb;
    return a > b;
}

int main(void)
{
    //array with MAX + 1 rows, and 8 columns, initialized to zero
    double arr[MAX + 1][8] = { 0 };
    FILE* file = fopen("temp.dat", "r");
    int count = 0;
    while(fscanf(file, "%lf %lf", &arr[count][0], &arr[count][1]) == 2)
    {
        count++;
        if(count == MAX) break;
    }

    qsort(arr, count, sizeof(arr[0]), compare_2d_array);

    //arr[0] and arr[1] are ready, now set up the other columns:
    for(int i = 0; i < count; i++)
    {
        //make modifications to other columns
        arr[i][2] = i ? arr[i - 1][0] : 0;
        arr[i][3] = arr[i][0];
        arr[i + 1][4] = i + 1;

        printf("%.6lf %.0lf %.6lf %.6lf %.0lf\n", 
                    arr[i][0], arr[i][1], arr[i][2], arr[i][3], arr[i][4]);
    }
    return 0;
}
person Barmak Shemirani    schedule 19.02.2018
comment
Я попробовал ваш код, и он фантастически работает с набором данных! Большое спасибо за вашу помощь. У меня есть несколько вопросов: не могли бы вы объяснить мне, почему возвращается m->y > n->y? в вашей функции compare()? Кроме того, объявление указателя const struct data_t *m = a помогает?? Является ли struct data_t набором данных с 200$ парами (x,y)? - person user177196; 19.02.2018
comment
Большое спасибо за вашу помощь, так как это совершенно ясно! У меня есть еще один вопрос: из двух столбцов предположим, что первый обозначает время $T$, а второй означает 1= Death occurs и 0 = Censor occurs. Я хочу создать два массива, в которых хранится следующая вычисляемая величина: первый массив хранит количество людей, которые живы и не подвергнуты цензуре между любыми двумя последовательными периоды времени, а второй массив хранит количество смертей, произошедших за те же периоды времени. Не могли бы вы помочь, как мне это сделать с циклами int array[MAX] и for? - person user177196; 19.02.2018
comment
Я попробовал for (int i = 0; i<m; i++){sum[i] = MAX - i;}, но тогда printf("%d", &sum[4]) ничего не выводит. Не совсем понял, почему он не хранится в массиве sum[]? Кроме того, я хочу сохранить первый столбец в массиве, где каждый элемент = последовательные периоды времени, конечные точки которых соответствуют 1 во втором столбце (таким образом, ни одна конечная точка не будет соответствовать 0). Но я наткнулся на то, как написать такое сложное условие, как это. Не могли бы вы помочь? - person user177196; 19.02.2018
comment
Я тоже пробовал printf("%d", sum[4]), но он все равно ничего не печатает (хотя сообщений об ошибках не появляется). В любом случае, не могли бы вы показать мне, как хранить последовательные периоды времени в массиве, у которого обе конечные точки равны 1 во втором столбце? Например, массив с элементами [0, 0.060493), [0.060493, 0.07085), [0.07085, 0.122609) и т. д. Я думаю, что мне, возможно, придется использовать 2-мерный массив, один из которых для хранения периода времени, другой для хранения результатов вычислений, и индексы этих двух точно совпадают ( т.е. для t\in [0, 0.060493) имеем result 1) - person user177196; 19.02.2018
comment
Давайте продолжим обсуждение в чате. - person user177196; 19.02.2018
comment
Посмотрите (Изменить 3 ****), включите это в свой новый вопрос вместе с входным образцом и ожидаемым результатом. - person Barmak Shemirani; 20.02.2018
comment
если я добавлю два дополнительных столбца в исходный набор данных, знаете ли вы, как расположить элементы в этих двух столбцах на основе результата сортировки первого столбца?? Ваша функция compare(), но с двумя дополнительными указателями в настройке ввода, все еще работает (т.е. `int compare(const void *a, const void *b, const void *c, const void *d)`)? - person user177196; 01.03.2018
comment
не могли бы вы помочь мне с моим вопросом в комментарии выше? Я изменил исходный набор данных для ясности. Я пытался написать int compare_4d_array(const void *a, const void *b, const void *c, const void *d), но это не сработало должным образом. - person user177196; 08.03.2018
comment
Я не собираюсь сортировать что-либо еще, кроме как изменить соответствующие строки записей в столбцах 2 и 3 выше на правильные после сортировки на основе столбцов 0 и 1 выше, как вы представили. Но тогда я бы начал новый вопрос. - person user177196; 09.03.2018
comment
Я запутался. Мой предыдущий комментарий не имеет смысла. У вас все еще есть один двумерный массив. Четырёхмерного массива нет. Сохраните функцию proc как compare_2d_array(const void *element1, const void *element2) - person Barmak Shemirani; 09.03.2018
comment
Вы можете задать новый вопрос на основе сортировки двумерного массива на основе 2 или более столбцов. Кроме того, в другом вопросе я показал, как сортировать структуру, это была аналогичная концепция. - person Barmak Shemirani; 09.03.2018
comment
Спасибо большое за помощь. Не могли бы вы поделиться со мной ссылкой на этот вопрос? - person user177196; 09.03.2018
comment
Я выложил новый! stackoverflow .com/questions/49183054/. Пожалуйста помоги. - person user177196; 09.03.2018

Для начала попробуйте:

double a;
int b;

...

fscanf(file, "%f %d", &a, &b)

Вы инициализировали a и b как тип double, но читаете целые числа (%d) из файла copy.dat. b является значением int, поэтому оставьте %d в fscanf и измените инициализацию.

person Tukanuk    schedule 19.02.2018
comment
Я понял. Благодаря тонну. Не могли бы вы помочь с функцией сортировки? - person user177196; 19.02.2018
comment
Поищите пузырьковую сортировку. Это не самая эффективная или красивая сортировка, но ее легко кодировать, и она будет работать с 200 значениями практически мгновенно. - person Tukanuk; 19.02.2018
comment
Чтобы массив y следовал виду массива x, примените логику поиска только к массиву x, но одновременно поменяйте местами массивы x и y. - person Tukanuk; 19.02.2018

Сначала добавьте && m < MAX в условия while, чтобы не переполнить буфер.

scanf ожидает с %d указатель на int, а не на double, и будет анализировать содержимое как целое число. Вы должны использовать %lf для разбора doubles. То же самое относится и к вызову printf:

while(fscanf(file, "%lf %lf", &a, &b) == 2 && m < MAX)
    ...

...

printf("%lf %lf\n", x[4], y[1]);

я получил

0.070850 0.000000

это пятая строка вашего файла данных (не четвертая, индексы массива начинаются с 0, а не с 1).

Что касается вашего другого вопроса:

Боюсь, вам придется написать свою собственную функцию сортировки.

person Pablo    schedule 19.02.2018
comment
У меня уже есть первая часть!! Огромное спасибо за помощь, Пабло. Я использую ОС Mac. Не могли бы вы помочь мне показать, как это сделать с qsort_r? - person user177196; 19.02.2018
comment
Как я уже сказал, qsort_r является расширением GNU компилятора GCC, вам нужно будет скомпилировать его с помощью компилятора GCC. Извините, что запутал вас, но тут даже qsort_r не поможет, потому что нужно знать, какие индексы в массиве сравниваются, а qsort передает значения, а не индексы. Боюсь, вам придется написать собственную функцию сортировки. - person Pablo; 19.02.2018