2010-07-13 13 views
1

所以我试图逐行阅读一个文本文件并将每行保存到一个char数组中。用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; 
} 
+1

你的文本文件是什么格式?它是ASCII还是Unicode(UTF-8或UTF-16)? 您是否还可以重新格式化代码,使其显示为代码并更具可读性 - 例如,每条语句一行。 谢谢。 – ChrisBD 2010-07-13 15:46:47

+1

我对代码进行了重新格式化,但for语句在各种编辑中以某种方式被肢解;请检查这是否是您正在使用的代码。 – 2010-07-13 15:52:33

+0

我不确定你的问题的解决方案是什么,因为我有类似的问题,但我想我会给一个小建议:你应该能够使用strdup而不是自己mallocing内存,然后使用strncopy。 strdup将使用malloc来分配内存并处理复制过程。只为你节省一条线,但我是内置函数的忠实粉丝。 – Chris 2013-10-13 00:27:04

回答

2

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

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

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

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

+1

也可以使用'memcpy',因为他总是复制'length'字符。 – 2010-07-13 16:13:07

+0

他已经为内存分配了长度+1 – 2010-07-13 16:29:12

+0

对,'NUL'需要+ 1。 – 2010-07-13 16:32:57

1

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

现在谈论复制一个字符串给大小:

至于我喜欢它,我会建议你使用 是strlcpy()而不是函数strncpy()

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

为函数strncpy不会用NULL终止字符串, strlcpy()解决了这个问题。

由strlcpy复制的字符串始终为NULL终止。

1

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

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

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

最后一个printf错了。您将只打印第一行。 修复上述问题后,循环遍历循环。坏朱朱的

2

一些情况下,最相关的一个是:

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

你刚刚宣布data作为一个可变长度数组零元素。尽管他们的名字,VLA不是真正可变的;分配后您不能更改数组的长度。所以,当你执行线

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

​​指的是你没有自己的内存,所以你调用未定义的行为。

由于您不知道您事先从文件中读取了多少行,因此需要创建一个可以动态扩展的结构。这里有一个例子:

/** 
* 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; 

你会分配阵列as

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++; 
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:为指向行的指针(例如数据[0]到数据[MAX_DATA_LINES])准备有效的内存存储空间。如果不这样做,你可能会遇到“分段错误”错误,如果你不这样做,那么你很幸运。

// 2:只是为了确保文件中行的总数是< MAX_DATA_LINES。您不会遇到'段错误'错误,因为指向行数据[> MAX_DATA_LINES]的内存存储不再有效。

0

我认为这可能是一个更快的实现,因为您不必将字节数组中的所有字符串的内容复制到辅助数组。你当然会失去你的'\ 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; 
}