2015-08-20 2 views
0

В настоящее время я изучаю C и хочу написать программу, которая читает строку чисел (из файла), разделенную пробелами (например: 43 2 6 120 5 23), а затем сохраняет эти числа в динамическом массиве. Моей идеей до сих пор является чтение строки в виде строки, выделите ее в токены (strtok), затем конвертируйте эти маркеры в номера int (atoi) и сохраните их. Моя проблема в том, что я должен выделить память, используя malloc, но не могу сказать программе, сколько памяти требуется вручную, поскольку она должна работать с линиями любой длины и чисел любого размера, а я не знаю, как это сделать. Я не предполагается использовать realloc.C: хранить токены в растущем массиве

Я не хочу, чтобы кто-то выполнял мою работу за меня, просто простой пример и/или хорошее объяснение способа сделать то, что я хочу, было бы действительно полезно. Если больше информации требуется, я дам ее вам. Я знаю, что здесь есть некоторые вопросы по этой теме, и я посмотрел на них, мне просто трудно понять некоторые вещи, не объяснив их, так как я совершенно не знаком с программированием на С. Конечно, если кто-нибудь может дать мне ссылку, которая могла бы мне помочь, это было бы здорово.

ответ

0

Вы можете написать отдельную функцию, которая будет извлекать числа из строки и хранить их в динамически распределенном массиве без использования realloc. Чтобы подсчитать числа в строке перед распределением массива, вы можете использовать стандартную функцию C sscanf.

Вот демонстративной программа

#include <stdlib.h> 
#include <stdio.h> 

size_t get_data(const char *s, int **a) 
{  
    int x; 
    size_t n; 
    int m; 

    *a = NULL; 
    n = 0; 

    for (const char *p = s; sscanf(p, "%d%n", &x, &m) == 1; p += m) ++n; 

    if (n && (*a = malloc(n * sizeof(int)))) 
    { 
     const char *p = s; 
     for (size_t i = 0; i < n; i++, p += m) sscanf(p, "%d%n", *a + i, &m); 
    } 

    return n; 
}  

int main(void) 
{ 
    char s[] = " 43 2 6 120 5 23"; 
    int *a; 
    size_t n = get_data(s, &a); 

    if (n) 
    { 
     for (size_t i = 0; i < n; i++) printf("%d ", a[i]); 
     printf("\n"); 
    }   

    free(a); 
} 

Выход программы

43 2 6 120 5 23 
+0

Спасибо за ваш ответ, это действительно помогло мне понять вещи лучше. Но не могли бы вы, или кто-нибудь, объяснить часть счета более тщательно? – 110100100

0

Если вы не возражаете о последовательных белых пространств, то можно просто подсчитать их количество и добавить один:

#include <stdio.h> 

int main(void) 
{ 
    char line[] = "43 2 6 120 5 23"; 

    size_t numCount = 1; 
    char *ptr = line; 
    while (*ptr != '\0') { 
     if (*ptr == ' ') { 
      numCount++; 
     } 
     ptr++; 
    } 

    // malloc call, etc. 

    return 0; 
} 
0

После прочтения строки, код нужно будет сканировать строку в два раза , один раз для подсчета чисел (чтобы можно было выделить правильный объем памяти) и снова для преобразования чисел и сохранения их в массиве.

Проблема с использованием strtok заключается в том, что он изменит строку, что предотвращает использование кода strtok для сканирования линии во второй раз.

Таким образом, решение состоит в том, чтобы написать простой цикл for, который анализирует линию и подсчитывает числа. Для этого вам нужна переменная, которая отслеживает, находитесь ли вы внутри числа или между номерами. Вот псевдокод:

state = outside 
count = 0 
for each character in the line 
    if (state == outside) 
    { 
     if (isdigit(character)) 
      count++ 
      state = inside 
    } 
    else 
    { 
     if (isspace(character)) 
      state = outside 
    } 

В конце цикла, count может быть использован для выделения памяти для массива, а затем вы можете использовать strtok и strtol для преобразования чисел. Обратите внимание, что я не учитывал проверку ошибок для случая, когда символ не является ни цифрой, ни пробелом.

1

Это довольно стандартное приложение для использования динамической памяти. То, что вы сделаете, - объявить память для начального числа целых чисел, прочитать до тех пор, пока вы не достигнете этого числа, перераспределите свой массив в два раза больше, чем это было, и продолжайте движение, пока вы не прочтете все свои данные.

При чтении всей строки данных в буфер, а затем разбор буфера для того, что вам нужно, как правило, является лучшей практикой, в этом случае fscanf был специально разработан для обработки и преобразования целочисленного значения (и автоматически будет использовать пробелы, разделяющие значения).

(calloc используется ниже для инициализации все элемента 0, malloc хорошо в этом примере, но инициализация предотвращает непреднамеренное чтение из неинициализированного элемента в более сложных ситуациях)

Вот короткий пример:

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

#define MAXI 64 

int main (int argc, char **argv) { 

    int *array = NULL; 
    size_t idx = 0, max_idx = 0; 
    size_t arraysize = MAXI; 
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; 

    if (!fp) { 
     fprintf (stderr, "error: file open failed. '%s'\n", argc > 1 ? argv[1] : "stdin"); 
     return 1; 
    } 

    /* allocate initial array */ 
    array = calloc (MAXI, sizeof *array); 

    /* read values from file */ 
    while (fscanf (fp, "%d", &array[idx]) == 1) { 
     idx++; 

     /* realloc if necessary */ 
     if (idx == arraysize) { 
      int *tmp = realloc (array, arraysize * sizeof *array * 2); 
      if (!tmp) { 
       fprintf (stderr, "error: realloc - virtual memory exhausted.\n"); 
       return 1; 
      } 
      array = tmp; 
      memset (array + arraysize, 0, arraysize); /* zero new memory */ 
      arraysize *= 2; 
     } 
    } 

    /* close file */ 
    if (fp != stdin) fclose (fp); 

    max_idx = idx; 

    /* print array */ 
    for (idx = 0; idx < max_idx; idx++) 
     printf (" array[%3zu] : %d\n", idx, array[idx]); 

    free (array); /* free memory */ 

    return 0; 
} 

Пример данных

$ cat dat/100intspace.txt 
27086 29317 32736 3356 12059 13921 9388 25672 19828 25390 -1190 25857 ... 

Выход

$ ./bin/array_dyn_read_int dat/100intspace.txt 
array[ 0] : 27086 
array[ 1] : 29317 
array[ 2] : 32736 
array[ 3] : 3356 
array[ 4] : 12059 
array[ 5] : 13921 
array[ 6] : 9388 
array[ 7] : 25672 
array[ 8] : 19828 
array[ 9] : 25390 
array[ 10] : -1190 
array[ 11] : 25857 
... 

Проверка объем память Используйте

Всякий раз, когда вы выделение памяти динамически, это ваша ответственность (1) отслеживать то, что вы выделяете; (2) сохранить указатель на начальный адрес (чтобы вы могли освободить его позже); и (3) освобождение памяти, когда она больше не нужна. valgrind или подобная ошибка проверки памяти просты в использовании и должны быть использованы для проверки Вашего использования памяти:

$ valgrind ./bin/array_dyn_read_int dat/100intspace.txt 
==7348== Memcheck, a memory error detector 
==7348== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. 
==7348== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info 
==7348== Command: ./bin/array_dyn_read_int dat/100intspace.txt 
==7348== 
array[ 0] : 27086 
array[ 1] : 29317 
array[ 2] : 32736 
array[ 3] : 3356 
array[ 4] : 12059 
array[ 5] : 13921 
array[ 6] : 9388 
array[ 7] : 25672 
array[ 8] : 19828 
array[ 9] : 25390 
array[ 10] : -1190 
array[ 11] : 25857 
.... 
==7348== 
==7348== HEAP SUMMARY: 
==7348==  in use at exit: 0 bytes in 0 blocks 
==7348== total heap usage: 3 allocs, 3 frees, 1,336 bytes allocated 
==7348== 
==7348== All heap blocks were freed -- no leaks are possible 
==7348== 
==7348== For counts of detected and suppressed errors, rerun with: -v 
==7348== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2) 

Позвольте мне знать, если у вас есть какие-либо вопросы.

Смежные вопросы