使用strncpy逐行将文件复制到char数组中

时间:2010-07-13 15:41:20

标签: c arrays null-terminated strncpy

所以我试图逐行读取文本文件并将每行保存到char数组中。

从我在循环中的打印输出中我可以看出它正在计算每行的行数和字符数,但是我遇到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 个答案:

答案 0 :(得分:2)

您的“data []”数组被声明为指向大小为0的字符的指针数组。当您为其指定指针时,它们没有空间。这可能会造成麻烦。

最简单的解决方法是对数组进行传递以确定行数,然后执行类似“char ** data = malloc(number_of_lines * sizeof(char *))”的操作。然后做“数据[计数器]”的分配将起作用。

你是对的strncpy()是一个问题 - 如果复制最大字节数,它将不会'\ 0'终止字符串。在strncpy()之后添加“data [counter] [length] ='\ 0';”

最后的printf()是错误的。要打印所有行,请使用“for(i = 0; i&lt; counter; i ++)printf(”%s \ n“,data [counter]);”

答案 1 :(得分:2)

几个坏juju的例子,最相关的是:

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 :(得分:1)

您正在尝试使用格式说明符%s打印数据,而您的数据是指向char的指针数组。

现在谈论复制带有大小的字符串:

据我所知,我建议你使用 strlcpy()而不是strncpy()

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

as strncpy不会以NULL结束字符串, strlcpy()解决了这个问题。

由strlcpy复制的

字符串始终为NULL终止。

答案 3 :(得分:1)

为变量data[counter]分配适当的内存。在您的情况下,计数器设置为0.因此,如果您尝试访问数据[1]等,它将给出分段错误。

声明像data [counter]这样的变量是一种不好的做法。即使计数器的后续流程中的计数器改变,也不会将内存分配给阵列数据。 因此,使用如上所述的双字符指针。

您可以使用现有循环首先查找行数。

最后一次打印错误。您将打印第一行。 修复上述问题后,迭代循环。

答案 4 :(得分:1)

更改

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:只是为了确保文件中的总行数是&lt; MAX_DATA_LINES。您不会遇到“分段错误”错误,因为指向行数据[&gt; MAX_DATA_LINES]的指针的内存存储不再有效。

答案 5 :(得分:0)

我认为这可能是一个更快的实现,因为您不必将所有字符串的内容从bytes数组复制到辅助数组。你当然会失去'\ n'字符。

它还考虑了不以新行字符结尾的文件,并且pos定义为用于bytes []的数组索引,长度也应该很长。

#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;
}