【问题标题】:How do I collect chars into a string in C?如何将字符收集到 C 中的字符串中?
【发布时间】:2021-12-08 00:55:00
【问题描述】:

我需要为我的词法分析器收集一些字符到缓冲区中,但我不知道如何。我已经阅读了有关 stackoverflow 的一些答案,但这些是不同的情况。我有一个读取下一个字符的 while 循环,我想将逻辑放入其中,以便将新字符附加到内存中的缓冲区。

// init buffer with the first char 'h'
char *buffer = malloc(sizeof(char));
buffer[0] = 'h';
buffer[1] = '\0';

// go through input char by char
while(...)
{
   char c = read_next_char(); 
   buffer.append(c) // I whould do in JavaScript, but not in C :(
}


【问题讨论】:

  • 开始你需要分配x的字节数并且每次读取char并将其存储到缓冲区。您还需要注意使用realloc 重新调整缓冲区的大小。在您的情况下,您分配单个字节 char *buffer = malloc(sizeof(char)); 并访问 buffer[1] 或任何其他索引是 UB
  • 您是要逐个字符地读取 C 类型字符串还是仅读取原始字符数组?
  • 看你自己的代码5秒。考虑到您之后对buffer 的处理,malloc 看起来是否正确?我假设你认为 C 有一个字符串类。它没有,它没有的字符串类不是char
  • 在谈论你自己的词法分析器之前,你必须先谈谈什么是字符串和字符。

标签: c char concatenation


【解决方案1】:

在您的情况下,您在开头分配一个字节 char *buffer = malloc(sizeof(char)); 并访问缓冲区 [1] 或任何其他索引是 UB。

您可以在开始时分配已知数量的字节并使用它,直到您看到需要更多缓冲区大小的点为止。

类似的,

int buffersize = 100;
int index =0;
char *buffer = malloc(sizeof(char)*buffersize); //100bytes are allocated

if(!buffer)
    return;

buffer[index++] = 'h';
buffer[index++] = '\0';

// go through input char by char
while(...)
{
   char c = read_next_char(); 
   if(index == buffersize ){
      buffersize +=100;
       buffer= realloc(buffer, buffersize );
      //here buffer size is increased by 100
       if(!buffer) 
           return;
   }
   
   buffer[index++] = c ;
}

注意:buffer 使用完毕后必须释放,否则会导致资源泄漏。

【讨论】:

    【解决方案2】:

    您需要简单地覆盖空终止字符并添加新的。

    char *append(char *buff, int ch)
    {
        size_t len = strlen(buff);
        buff[len] = ch;
        buff[len+1] = 0;
        return buff;
    }
    

    代码假定buff 是一个有效指针,指向足够长的内存块以容纳新字符和空终止字符。它必须包含一个有效的 C 字符串。

    【讨论】:

    • 对于较长的字符串,这将变得非常低效。看到这篇很好的文章:joelonsoftware.com/2001/12/11/back-to-basics
    • @Jabberwocky 这只是方法的指示。当然,有很多方法可以更有效地做到这一点。但理念是一样的。
    • @Jabberwocky 这取决于字符来自哪里......如果它是某种 IO 流(特别是如果它是标准输入),因为 IO 很慢并不重要。在这种情况下,让事情像这样简单是很好的。
    • 另一种实现方式可能是:char *append(char *buff, int ch) { char str[2] = {ch, '\0'}; return strcat(buff, str); }
    【解决方案3】:

    与 java 或 javascipt 不同,C 中没有字符串类型,您需要自己编写。

    这是一个非常简单的示例,说明如何以有效的方式处理字符串的构建。

    这很自我解释。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct DynamicString
    {
      char* string;    // pointer to string
      int length;      // string length
      int capacity;    // capacity of the string buffer (= allocated size)
    };
    
    #define DS_CHUNKSIZE 100   // we increase the buffer size by DS_CHUNKSIZE
                               // change this as needed
    
    // Initialize the structure
    void InitDynamicString(struct DynamicString* ds)
    {
      ds->capacity = DS_CHUNKSIZE + 1;
      ds->string = malloc(ds->capacity);
      ds->string[0] = 0;   // null terminator
      ds->length = 0;      // initial string length 0
    };
    
    // Increase the string buffer size if necessary
    // (internal function)
    void IncreaseSize(struct DynamicString* ds, int newsize)
    {
      if (ds->length + newsize + 1 > ds->capacity)
      {
        ds->capacity = ds->length + newsize + DS_CHUNKSIZE + 1;
        ds->string = realloc(ds->string, ds->capacity); // reallocate a new larger buffer
      }
    }
    
    // append a single character
    void AppendChar(struct DynamicString* ds, char ch)
    {
      IncreaseSize(ds, sizeof(char)); // increase size by 1 if necessary
      ds->string[ds->length++] = ch;  // append char
      ds->string[ds->length] = 0;     // null terminator
    }
    
    // append a string
    void AppendString(struct DynamicString* ds, const char *str)
    {
      IncreaseSize(ds, strlen(str));  // increase by length of string if necessary
      strcat(ds->string, str);        // concatenate
      ds->length += strlen(str);      // update string length
    }
    
    
    int main(int argc, char* argv[])
    {
      struct DynamicString ds;
    
      InitDynamicString(&ds);   // initialize ds
    
      AppendChar(&ds, 'a');     // append chars
      AppendChar(&ds, 'b');
      AppendChar(&ds, 'c');
    
      AppendString(&ds, "DE");      // append strings
      AppendString(&ds, "xyz1234");
    
      printf("string = \"%s\"", ds.string);  // show result
    }
    

    你的代码可以这样使用它:

    struct DynamicString buffer;
    InitDynamicString(&buffer)
    
    dAppendChar(&buffer, 'h');
    
    while(...)
    {
       char c = read_next_char(); 
       AppendChar(&buffer, c); // quite similar to  buffer.append(c)
    }
    

    免责声明:

    • 代码未经彻底测试,可能存在错误。
    • 没有任何错误检查。 mallocrealloc 可能会失败。
    • 其他有用的函数如SetString(struct DynamicString *ds, const char *string)需要编写。
    • 还有优化的空间,尤其是strcat 可以有不同的处理方式,请阅读this article 了解更多信息。我把它作为一个(非常简单的)练习留给读者。

    【讨论】:

    • 次要:strcat 可以替换为strcpy
    • @4386427 是的,这是免责声明的第三点
    【解决方案4】:

    C 中没有可以将字符附加到字符串的标准函数。您需要从头开始编写代码。

    让我们从这里开始:

    char *buffer = malloc(sizeof(char));  // This allocates memory for ONE char
    buffer[0] = 'h';                      // So this is fine
    buffer[1] = '\0';                     // but this is bad. It writes outside the allocated memory
    

    通过为两个字符分配内存来修复它

    char *buffer = malloc(2);  // sizeof(char) is always 1 so no need for it
    buffer[0] = 'h';
    buffer[1] = '\0';
    

    当您想在字符串中追加一个新字符时,您还需要为其分配内存。换句话说,您需要增加buffer 指向的内存大小。为此,您可以使用函数realloc

    size_t buffer_size = 2;
    char *buffer = malloc(buffer_size );
    buffer[0] = 'h';
    buffer[1] = '\0';
    
    while(...)
    {
        char c = read_next_char(); 
    
        char* tmp = realloc(buffer, buffer_size + 1);
        if (tmp == NULL)
        {
            // realloc failed ! Add error handling here
            ... error handling ...
        }
        buffer = tmp;
        buffer[buffer_size - 1] = c;  // Add the new char
        buffer[buffer_size] = '\0';     // Add the string termination
        ++buffer_size;                  // Update buffer size
    }
    

    【讨论】:

      【解决方案5】:

      其他答案也可以,但它们很复杂。我建议一个更简单的解决方案。字符串是char 的数组,其中该字符串的最后一个char'\0'-Byte。在它之后的数组中可以有更多的char,但它们不是字符串的一部分。

      更简单的解决方案是创建一个足以容纳 98% 的情况的数组,用它来存储字符串,当字符串变得太长时,您可以退出并出现错误。在需要时更改缓冲区大小是一个不错的功能,但是当您不熟悉 C 时,您不应该从那里开始。

      #define BUFFER_SIZE 1024
      // init buffer with the first char 'h'
      char buffer[BUFFER_SIZE];
      buffer[0] = 'h';
      buffer[1] = '\0';
      
      // go through input char by char Replace the ... with your condition of the while loop
      for(size_t i=1;...;i++) //start at 1 so the 'h' is not overwritten
      {
         if(i==BUFFER_SIZE-1) //-1 for the '\0'-Byte
         {
           fputs("Input too long, exit\n",stderr);
           exit(1);
         }
         //Are you sure you don't need error handling for read_next_char()?
         buffer[i] = read_next_char();
         buffer[i+1]='\0'; //End the string with a '\0'-Byte
      }
      

      【讨论】:

        猜你喜欢
        • 2020-08-16
        • 2021-12-30
        • 1970-01-01
        • 2022-08-20
        • 2015-03-09
        • 1970-01-01
        • 1970-01-01
        • 2019-12-23
        • 1970-01-01
        相关资源
        最近更新 更多