Я хочу реализовать поисковую таблицу и вот данные:
20130610 Diamond CoinMate 11.7246 15.7762 2897
20130412 Diamond Bithumb 0.209 0.2293 6128
20130610 OKCash Bithumb 0.183 0.2345 2096
20130412 Ethereum Chbtc 331.7282 401.486 136786
20170610 OKCash Tidex 0.0459 0.0519 66
...
и мой код
typedef struct data{
int *date;
string currency[100];
string exchange[100];
double *low;
double *high;
int *daily_cap;
} Data;
int main()
{
FILE *fp = fopen("test_data.txt", "r");
Data tmp[50];
int i = 0;
while (!feof(fp)){
fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
i++;
}
fclose(fp);
}
но первая проблема заключается в том, что я не могу создать большой массив для хранения моей структуры вроде
Data tmp[1000000]
и даже я пробую всего 50 элементов, программа ломается, когда завершается main (). Может ли кто-нибудь сказать, как это исправить, или дать мне лучший метод, спасибо.





Вы не можете сканировать значение в незанятое пространство, другими словами, вам нужно место для всех этих указателей в struct, переключитесь на
typedef struct data{
int date;
string currency[100];
string exchange[100];
double low;
double high;
int daily_cap;
} Data;
Или используйте malloc, чтобы назначить место этим указателям перед их использованием.
while (!feof(fp)){
tmp[i].date = malloc(sizeof(int));
...
Но в этом случае вам не нужно передавать адрес таких членов в fscanf, поскольку они уже являются указателями:
fscanf(fp, "%d%s%s%f%f%7d", &tmp[i].date, ..
должно быть
fscanf(fp, "%d%s%s%lf%lf%7d", tmp[i].date, ...
Обратите внимание, что double хочет %lf вместо %f.
Это тоже очень сбивает с толку:
typedef struct data{
int *date;
string currency[100];
...
string - это typedef из char? Я думаю, вы имеете в виду string currency;, поскольку string обычно является псевдонимом char *, в этом случае вам также понадобится место для этого участника: currency = malloc(100);
Наконец, взгляните на Почему «while (! Feof (file))» всегда неверно?
В коротком фрагменте слишком много ошибок, я предлагаю вам прочитать хорошую книгу C.
Ваш код исправлен с использованием динамической памяти, которая позволяет вам зарезервировать место для большого количества данных (см. Другой ответ @LuisColorado) и использовать fgets и sscanf вместо fscanf:
#include <stdio.h>
#include <stdlib.h>
typedef struct data{
int date;
char currency[100];
char exchange[100];
double low;
double high;
int daily_cap;
} Data;
int main(void)
{
FILE *fp = fopen("test_data.txt", "r");
/* Always check the result of fopen */
if (fp == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
Data *tmp;
tmp = malloc(sizeof(*tmp) * 50);
if (tmp == NULL) {
perror("malloc");
exit(EXIT_FAILURE);
}
char buf[512];
int i = 0;
/* Check that you don't read more than 50 lines */
while ((i < 50) && (fgets(buf, sizeof buf, fp))) {
sscanf(buf, "%d%99s%99s%lf%lf%7d", &tmp[i].date, tmp[i].currency, tmp[i].exchange, &tmp[i].low, &tmp[i].high, &tmp[i].daily_cap);
i++;
}
fclose(fp);
/* Always clean what you use */
free(tmp);
return 0;
}
спасибо, ваш ответ действительно полезен, я бы пошел почитать хорошую книгу C.
Конечно не можешь. Представьте, что вы создаете массив регистров 1.0E6 для sizeof (Data), размер которого, как я полагаю, составляет не менее 32 (четыре указателя) и 200 байтов (не менее этого, поскольку вы не даете определение типа string), а это 232 МБ ( по крайней мере) на 64-байтовой машине (в 32-битной это 216 МБ) и что в случае, если тип string имеет ширину только один символ (чего я боюсь, нет). В случае, если строка является typedef char *, тогда у вас есть 432 указателя в вашей структуре что дает 432MB байта в только одна переменная. Затем, если вы объявляете эту абсолютно огромную переменную как локальную переменную, вы должны знать, что размер стека в большинстве операционных систем unix ограничен примерно 8 МБ, а это означает, что вам нужно создать свою программу со специальными параметрами, чтобы разрешить больший стек max. размер. И вам, вероятно, понадобится ваша учетная запись, чтобы увеличить до этого размера также ulimit, чтобы ядро позволяло вам иметь такой большой сегмент размера стека.
Пожалуйста, в следующий раз сообщите нам полная информация, так как незнание определения типа string или публикация неполной программы позволяет нам только гадать, что может происходить, и не обнаруживать фактические ошибки. Это заставляет вас тратить свое время, и для нас то же самое. Спасибо.
Если ваш список currency и exchange известен заранее, то нет необходимости выделять или хранить какие-либо массивы в вашем struct. Списки могут быть глобальными массивами указателей на строковые литералы, и все, что вам нужно сделать, это сохранить указатель на литерал как для currency, так и для exchange (вы даже можете сэкономить еще несколько байтов, сохранив индекс вместо указателя).
Например, ваши списки бирж могут быть сохранены один раз следующим образом:
const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
*exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };
(если число того требует, выделите место для строк и прочтите их из файла)
Теперь у вас есть все возможные строки для currency и exchange, все, что вам нужно в вашей структуре data, - это указатель для каждой, например
typedef struct {
const char *currency, *exchange;
double low, high;
unsigned date, daily_cap;
} data_t;
(unsigned дает лучший диапазон, и нет отрицательных dates или daily_cap)
Теперь просто объявите массив data_t (или выделите для них, в зависимости от количества). Ниже для примера приведен простой массив автоматического хранения. Например.
#define MAXD 128
...
data_t data[MAXD] = {{ .currency = NULL }};
Поскольку вы читаете «строки» данных, fgets или POSIX getline являются строчно-ориентированными вариантами. После прочтения строки вы можете проанализировать строку с помощью sscanf, используя временные значения, сравнить, совпадают ли значения для currency и exchange, прочитанные из сохраненного файла, а затем назначить указатель на соответствующую строку вашей структуре, например
int main (void) {
char buf[MAXC] = "";
size_t n = 0;
data_t data[MAXD] = {{ .currency = NULL }};
while (n < MAXD && fgets (buf, MAXC, stdin)) {
char curr[MAXE] = "", exch[MAXE] = "";
int havecurr = 0, haveexch = 0;
data_t tmp = { .currency = NULL };
if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date,
curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
for (int i = 0; i < NELEM(currency); i++) {
if (strcmp (currency[i], curr) == 0) {
tmp.currency = currency[i];
havecurr = 1;
break;
}
}
for (int i = 0; i < NELEM(exchange); i++) {
if (strcmp (exchange[i], exch) == 0) {
tmp.exchange = exchange[i];
haveexch = 1;
break;
}
}
if (havecurr & haveexch)
data[n++] = tmp;
}
}
...
Вкратце, вы можете сделать что-то похожее на следующее:
#include <stdio.h>
#include <string.h>
#define MAXC 256
#define MAXD 128
#define MAXE 32
#define NELEM(x) (int)(sizeof (x)/sizeof (*x))
const char *currency[] = { "Diamond", "OKCash", "Ethereum" },
*exchange[] = { "CoinMate", "Bithumb", "Chbtc", "Tidex" };
typedef struct {
const char *currency, *exchange;
double low, high;
unsigned date, daily_cap;
} data_t;
int main (void) {
char buf[MAXC] = "";
size_t n = 0;
data_t data[MAXD] = {{ .currency = NULL }};
while (n < MAXD && fgets (buf, MAXC, stdin)) {
char curr[MAXE] = "", exch[MAXE] = "";
int havecurr = 0, haveexch = 0;
data_t tmp = { .currency = NULL };
if (sscanf (buf, "%u %31s %31s %lf %lf %u", &tmp.date,
curr, exch, &tmp.low, &tmp.high, &tmp.daily_cap) == 6) {
for (int i = 0; i < NELEM(currency); i++) {
if (strcmp (currency[i], curr) == 0) {
tmp.currency = currency[i];
havecurr = 1;
break;
}
}
for (int i = 0; i < NELEM(exchange); i++) {
if (strcmp (exchange[i], exch) == 0) {
tmp.exchange = exchange[i];
haveexch = 1;
break;
}
}
if (havecurr & haveexch)
data[n++] = tmp;
}
}
for (size_t i = 0; i < n; i++)
printf ("%u %-10s %-10s %8.4f %8.4f %6u\n", data[i].date,
data[i].currency, data[i].exchange, data[i].low,
data[i].high, data[i].daily_cap);
}
Пример использования / вывода
$ ./bin/coinread <dat/coin.txt
20130610 Diamond CoinMate 11.7246 15.7762 2897
20130412 Diamond Bithumb 0.2090 0.2293 6128
20130610 OKCash Bithumb 0.1830 0.2345 2096
20130412 Ethereum Chbtc 331.7282 401.4860 136786
20170610 OKCash Tidex 0.0459 0.0519 66
При таком подходе, независимо от того, выделяете ли вы для своего массива структуры или используете автоматическое хранилище, вы минимизируете размер хранимых данных, не дублируя хранилище известных значений. На x86_64 размер вашей структуры data_t будет примерно 40 байт. В среднем, используя стек 1-4 Megabyte, вы можете безопасно хранить множество структур 40-byte, прежде чем вам понадобится начать выделение. Вы всегда можете начать с автоматического хранения, и если вы достигнете некоторого процента доступного пространства стека, динамически распределите memcpy, установите флаг, чтобы указать, какое хранилище используется, и продолжайте ...
Большие структуры данных должны быть созданы как локальные переменные никогда (т.е. автоматическая продолжительность хранения). Всегда используйте динамическое размещение для больших структур данных, например
mallocи других. Создание больших структур данных в качестве локальных переменных вызовет переполнение стека и сбой программы.