【问题标题】:Copying a file line by line into a char array with strncpy使用 strncpy 将文件逐行复制到 char 数组中
【发布时间】:2010-07-13 15:41:19
【问题描述】:

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

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

【问题讨论】:

  • 你的文本文件是什么格式的?它是 ASCII 还是 Unicode(UTF-8 或 UTF-16)?您是否还可以重新格式化您的代码,使其显示为代码并且更具可读性 - 例如,每条语句一行。谢谢。
  • 我重新格式化了代码,但是 for 语句在各种编辑中都被破坏了;请检查这是否真的是您正在使用的代码。
  • 我不确定您的问题的解决方案是什么,因为我遇到了类似的问题,但是我想我会给出一个小提示:您应该能够使用 strdup 而不是 mallocing自己的内存,然后使用strncopy。 strdup 将使用 malloc 分配内存并处理复制过程。只会为您节省一行,但我是内置函数的忠实粉丝。

标签: c arrays null-terminated strncpy


【解决方案1】:

您的“data[]”数组被声明为一个指向大小为 0 的字符的指针数组。当您为它分配指针时,它们没有空间。这可能会带来无穷无尽的麻烦。

最简单的解决方法是遍历数组以确定行数,然后执行类似“char **data = malloc(number_of_lines * sizeof(char *))”的操作。然后做“数据[计数器]”的分配就可以了。

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

最后的 printf() 是错误的。要打印所有行,请使用 "for (i = 0; i

【讨论】:

  • 也可以使用memcpy,因为他总是准确地复制length 字符。
  • 他为长度+1分配了内存
  • 对,NUL 需要 + 1。
  • 是的,因此 '\0' 将被复制就是我的意思:)。
【解决方案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++;

【讨论】:

    【解决方案3】:

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

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

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

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

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

    由 strlcpy 复制的字符串总是以 NULL 结尾。

    【讨论】:

      【解决方案4】:

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

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

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

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

      【讨论】:

        【解决方案5】:

        改变

        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] 的指针的内存存储不再有效。

        【讨论】:

          【解决方案6】:

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

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

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

          【讨论】:

            猜你喜欢
            • 2013-01-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-01-20
            • 2014-04-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多