1

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

从循环中的打印输出中,我可以看出它正在正确计算行数和每行的字符数,但我遇到了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;
}
4

6 回答 6

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]);"

于 2010-07-13T16:03:49.230 回答
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++;
于 2010-07-13T17:20:21.287 回答
1

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

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

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

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

由于 strncpy 不会以 NULL 终止字符串,因此 strlcpy() 解决了这个问题。

strlcpy 复制的字符串始终以 NULL 结尾。

于 2010-07-13T16:33:28.243 回答
1

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

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

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

最后一个 printf 是错误的。您将只打印第一行。解决上述问题后,迭代循环。

于 2010-07-13T16:35:22.000 回答
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:只是为了确保如果文件中的总行数 < MAX_DATA_LINES。您不会遇到“分段错误”错误,因为指向行数据[>MAX_DATA_LINES] 的指针的内存存储不再有效。

于 2010-07-13T17:23:50.687 回答
0

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

它还考虑了不以换行符结尾的文件,并且 pos 被定义为 long 用于 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;
}
于 2010-07-13T23:24:25.433 回答