Копирование файла строка за строкой в ​​массив char с помощью strncpy - PullRequest
1 голос
/ 13 июля 2010

Итак, я пытаюсь читать текстовый файл построчно и сохранять каждую строку в массиве символов.

Из моей распечатки в цикле я могу сказать, что она считает строки и количество символов вправильно, но у меня проблемы с strncpy.Когда я пытаюсь распечатать массив данных, он отображает только 2 странных символа.Я никогда не работал с strncpy, поэтому я чувствую, что моя проблема может быть связана с нулевым завершением.

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

int main(int argc, char* argv[])
{
    FILE *f = fopen("/home/tgarvin/yes", "rb");
    fseek(f, 0, SEEK_END);
    long pos = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *bytes = malloc(pos); fread(bytes, pos, 1, f);
    int i = 0; 
    int counter = 0; 
    char* data[counter]; 
    int length; 
    int len=strlen(data); 
    int start = 0;
    int end = 0;

    for(; i<pos; i++)
    {
        if(*(bytes+i)=='\n'){
            end = i;
            length=end-start;
            data[counter]=(char*)malloc(sizeof(char)*(length)+1);
            strncpy(data[counter], bytes+start, length);
            printf("%d\n", counter);
            printf("%d\n", length);
            start=end+1;
            counter=counter+1;
        }
    }
    printf("%s\n", data);
    return 0;
}

Ответы [ 6 ]

2 голосов
/ 13 июля 2010

Несколько случаев плохого джиу-джуджа, наиболее подходящим из которых является:

int counter = 0;  
char* data[counter];  

Вы только что объявили data как массив переменной длины с нулевыми элементами .Несмотря на свое название, VLA не являются действительно переменными;Вы не можете изменить длину массива после его выделения.Поэтому, когда вы выполняете строки,

data[counter]=(char*)malloc(sizeof(char)*(length)+1);   
strncpy(data[counter], bytes+start, length);   

data[counter] ссылается на память, которой вы не владеете, вы вызываете неопределенное поведение.

Поскольку вы не знаете, сколькоСтроки, которые вы читаете из файла заранее, вам нужно создать структуру, которая может динамически расширяться.Вот пример:

/**
 * Initial allocation of data array (array of pointer to char)
 */
 char **dataAlloc(size_t initialSize)
 {
   char **data= malloc(sizeof *data * initialSize);
   return data;
 }

 /**
  * Extend data array; each extension doubles the length
  * of the array.  If the extension succeeds, the function
  * will return 1; if not, the function returns 0, and the 
  * values of data and length are unchanged.
  */
 int dataExtend(char ***data, size_t *length)
 {
   int r = 0;
   char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
   if (tmp)
   {
     *length= 2 * *length;
     *data = tmp;
     r = 1;
   }
   return r;
 }

Затем в вашей основной программе вы объявите data как

char **data;

с отдельной переменной для отслеживания размера:

size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;

Вы бы изначально выделили массив как

data = dataAlloc(dataLength);

.Затем в цикле вы сравниваете свой счетчик с текущим размером массива и расширяете массив, когда они сравниваются равными, например:

if (counter == dataLength)
{
  if (!dataExtend(&data, &dataLength))
  {
    /* Could not extend data array; treat as a fatal error */
    fprintf(stderr, "Could not extend data array; exiting\n");
    exit(EXIT_FAILURE);
  }
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
  strncpy(data[counter], bytes+start, length); 
  data[counter][length] = 0; // add the 0 terminator
}
else
{
  /* malloc failed; treat as a fatal error */
  fprintf(stderr, "Could not allocate memory for string; exiting\n");
  exit(EXIT_FAILURE);
}
counter++;
2 голосов
/ 13 июля 2010

Ваш массив "data []" объявляется как массив указателей на символы размера 0. Когда вы назначаете указатели на него, для них нет места. Это не может стать причиной неприятностей.

Самым простым решением было бы сделать проход по массиву, чтобы определить количество строк, а затем сделать что-то вроде «char ** data = malloc (number_of_lines * sizeof (char *))». Тогда выполнение заданий «data [counter]» будет работать.

Вы правы в том, что strncpy () является проблемой - он не завершит строку '\ 0', если скопирует максимальное количество байтов. После strncpy () добавьте "data [counter] [length] = '\ 0';"

printf () в конце неверен. Чтобы напечатать все строки, используйте "for (i = 0; i

1 голос
/ 13 июля 2010

Изменение

int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
      strncpy(data[counter], bytes+start, length);
...

до

int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
       strncpy(data[counter], bytes+start, length);
...

// 1: подготовить допустимое хранилище для указателей на строки (например, data [0] - data [MAX_DATA_LINES]). Без этого вы можете столкнуться с ошибкой «ошибка сегментации», в противном случае вам повезет.

// 2: просто чтобы убедиться, что если общее количество строк в файле MAX_DATA_LINES] больше не действительна.

1 голос
/ 13 июля 2010

Выделите соответствующую память для переменной data[counter]. В вашем случае счетчик установлен на 0. Следовательно, он даст ошибку сегментации, если вы попытаетесь получить доступ к данным [1] и т. Д.

Объявление переменной типа data [counter] - плохая практика. Даже если счетчик изменится в последующем потоке программы, было бы бесполезно выделять память для данных массива. Следовательно, используйте двойной символ указателя, как указано выше.

Вы можете использовать существующий цикл, чтобы сначала найти количество строк.

Последний printf неверен. Вы будете печатать только первую строку с ним. Повторяйте цикл, как только вы исправите вышеуказанную проблему.

1 голос
/ 13 июля 2010

Вы пытаетесь распечатать данные с помощью спецификатора формата% s, в то время как ваши данные представляют собой массив указателей s на символ.

Теперь поговорим о копировании строки с заданным размером:

Насколько мне нравится, я бы посоветовал вам использовать strlcpy () вместо strncpy ()

size_t strlcpy( char *dst, const char *src, size_t siz);

, поскольку strncpy не завершит строку с помощью NULL, strlcpy () решает эту проблему.

строки, скопированные strlcpy, всегда заканчиваются на NULL.

0 голосов
/ 14 июля 2010

Я думаю, что это может быть более быстрой реализацией, поскольку вам не придется копировать содержимое всех строк из массива байтов во вторичный массив.Вы, конечно, потеряете символы '\ n'.

Он также учитывает файлы, которые не заканчиваются символом новой строки и поскольку pos определяется как длинный индекс массива, используемый для байтов []а также длина должна быть длинной.

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

#define DEFAULT_LINE_ARRAY_DIM 100

int main(int argc, char* argv[])
{
    FILE *f = fopen("test.c", "rb");
    fseek(f, 0, SEEK_END);
    long pos = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
    fread(bytes, pos, 1, f);
    if (bytes[pos-1]!='\n')
    {
        bytes[pos++] = '\n';
    }
    long i;
    long length = 0;
    int counter = 0;
    size_t size=DEFAULT_LINE_ARRAY_DIM;
    char** data=malloc(size*sizeof(char*));
    data[0]=bytes;

    for(i=0; i<pos; i++)
    {
        if (bytes[i]=='\n') {
            bytes[i]='\0';
            counter++;
            if (counter>=size) {
                size+=DEFAULT_LINE_ARRAY_DIM;
                data=realloc(data,size*sizeof(char*));
                if (data==NULL) {
                    fprintf(stderr,"Couldn't allocate enough memory!\n");
                    exit(1);
                }
            }
            data[counter]=&bytes[i+1];
            length = data[counter] - data[counter - 1] - 1;
            printf("%d\n", counter);
            printf("%ld\n", length);
        }
    }

    for (i=0;i<counter;i++)
        printf("%s\n", data[i]);

    return 0;
}
...