将制表符展开到C中的空格?

将制表符展开到C中的空格?,c,tabs,C,Tabs,我需要在输入行中展开制表符,以便它们是空格(宽度为8列)。我用以前的代码尝试过,我将每行大于10个字符的最后一个空格替换为“\n”以生成新行。在C语言中有没有办法将制表符设为8个空格以便展开它们?我的意思是,我确信这很简单,我就是不明白 这是我的密码: int v = 0; int w = 0; int tab; extern char line[]; while (v < length) { if(line[v] == '\t') tab = v; if (

我需要在输入行中展开制表符,以便它们是空格(宽度为8列)。我用以前的代码尝试过,我将每行大于10个字符的最后一个空格替换为“\n”以生成新行。在C语言中有没有办法将制表符设为8个空格以便展开它们?我的意思是,我确信这很简单,我就是不明白

这是我的密码:

int v = 0;
int w = 0;
int tab;
extern char line[];

while (v < length) {

   if(line[v] == '\t')
      tab = v;

   if (w == MAXCHARS) {
      // THIS IS WHERE I GET STUCK
      line[tab] = ' ';
      // set y to 0, so loop starts over
      w = 0;
   }
   ++v;
   ++w;
}
intv=0;
int w=0;
int选项卡;
外部字符行[];
while(v
这不是关于C语言的问题;这是一个关于找到正确算法的问题——你可以在任何语言中使用该算法

无论如何,如果不将
行[]
重新分配到一个更大的缓冲区(除非它是一个较大的固定长度,在这种情况下,您需要担心溢出),您根本无法做到这一点;在扩展选项卡时,需要更多内存来存储新的、更大的行,因此,像您尝试这样的字符替换根本不起作用

我的建议是:与其试着就地操作(甚至试着在内存中操作),我建议把它写成一个过滤器——从stdin中读取数据,然后一次向stdout写入一个字符;这样,您就不必担心内存分配或释放,也不必担心行[]长度的变化


如果使用此代码的上下文要求它在内存中运行,请考虑实现类似于RealCube()/代码>的API,其中返回一个新指针;如果不需要更改正在处理的字符串的长度,只需保留原始内存区域即可,但如果确实需要调整其大小,则可以使用此选项。

我可能会这样做:

 make CFLAGS="-DTEST -DTEST_INPUT_BUFFERSIZE=32 -DTEST_OUTPUT_BUFFERSIZE=32" dt
  • 遍历字符串一次,只计算制表符(如果您还不知道,还包括字符串长度)
  • 分配
    original\u size+7*个字节的\u选项卡
    bytes内存(其中original\u size计算空字节)
  • 再次遍历字符串,将每个非制表符字节复制到新内存中,并为每个制表符插入8个空格

  • 如果要就地替换而不是创建新字符串,则必须确保传入的指针指向一个具有足够内存的位置以存储新字符串(该位置将比原始位置长,因为8个空格或7个字节多于一个制表符)。

    需要一个单独的缓冲区将输出写入,由于它通常比输入更长:

    void detab(char* in, char* out, size_t max_len) {
        size_t i = 0;
        while (*in && i < max_len - 1) {
            if (*in == '\t') {
                in++;
                out[i++] = ' ';
                while (i % 8 && i < max_len - 1) {
                    out[i++] = ' ';
                }
            } else {
                out[i++] = *in++;
            }
        }
    
        out[i] = 0;
    }
    
    void detab(char*in,char*out,size\u t max\u len){
    尺寸i=0;
    而(*in&&i
    您必须为
    out
    预先分配足够的空间(在最坏的情况下可能是
    8*strlen(in)+1
    ),并且
    out
    不能与
    in
    相同


    编辑:正如Jonathan Leffler所建议的那样,
    max_len
    参数现在可以确保避免缓冲区溢出。结果字符串将始终以null结尾,即使它被缩短以避免这种溢出。(我还重命名了该函数,并将
    int
    更改为
    size\u t
    ,以增加正确性:)

    未测试,但类似的操作应该可以:

    int v = 0;
    int tab;
    extern char line[];
    
    while (v < length){
      if (line[v] == '\t') {
        tab = (v % TAB_WIDTH) || TAB_WIDTH;
        /* I'm assuming MAXCHARS is the size of your array. You either need
         * to bail, or resize the array if the expanding the tab would make
         * the string too long. */
        assert((length + tab) < MAXCHARS);
        if (tab != 1) {
          memmove(line + v + tab - 1, line + v, length - v + 1);
        }
        memset(line + v, ' ', tab);
        length += tab - 1;
        v += tab;
      } else {
        ++v;
      }
    }
    
    intv=0;
    int选项卡;
    外部字符行[];
    while(v

    注意,这是O(n*m),其中n是行大小,m是制表符的数量。这在实践中可能不是问题。

    将字符串中的制表符转换为1-8个空格的方法有很多种。有一些低效的方法可以就地进行扩展,但最简单的方法是使用一个函数来处理输入字符串和一个单独的输出缓冲区,该缓冲区对于扩展字符串来说足够大。如果输入是6个制表符加上一个X和一个换行符(8个字符+终止null),那么输出将是48个空格、X和一个换行符(50个字符+终止null)-因此您可能需要比输入缓冲区大得多的输出缓冲区

    #include <stddef.h>
    #include <assert.h>
    
    static int detab(const char *str, char *buffer, size_t buflen)
    {
        char *end = buffer + buflen;
        char *dst = buffer;
        const char *src = str;
        char c;
    
        assert(buflen > 0);
        while ((c = *src++) != '\0' && dst < end)
        {
             if (c != '\t')
                 *dst++ = c;
             else
             {
                 do
                 {
                     *dst++ = ' ';
                 } while (dst < end && (dst - buffer) % 8 != 0);
             }
        }
        if (dst < end)
        {
            *dst = '\0';
            return(dst - buffer);
        }
        else
            return -1;
    }
    
    #ifdef TEST
    #include <stdio.h>
    #include <string.h>
    
    #ifndef TEST_INPUT_BUFFERSIZE
    #define TEST_INPUT_BUFFERSIZE 4096
    #endif /* TEST_INPUT_BUFFERSIZE */
    #ifndef TEST_OUTPUT_BUFFERSIZE
    #define TEST_OUTPUT_BUFFERSIZE (8 * TEST_INPUT_BUFFERSIZE)
    #endif /* TEST_OUTPUT_BUFFERSIZE */
    
    int main(void)
    {
         char ibuff[TEST_INPUT_BUFFERSIZE];
         char obuff[TEST_OUTPUT_BUFFERSIZE];
    
         while (fgets(ibuff, sizeof(ibuff), stdin) != 0)
         {
              if (detab(ibuff, obuff, sizeof(obuff)) >= 0)
                  fputs(obuff, stdout);
              else
                  fprintf(stderr, "Failed to detab input line: <<%.*s>>\n",
                          (int)(strlen(ibuff) - 1), ibuff);
         }
         return(0);
     }
     #endif /* TEST */
    

    如果要在此文件之外使用“detab()”函数,则需要创建一个包含其声明的头,并将该头包含在该代码中,当然,该函数不会是静态的。

    这是一个可重入的递归版本,它会自动分配正确大小的缓冲区:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct state
    {
        char *dest;
        const char *src;
        size_t tab_size;
        size_t size;
        _Bool expand;
    };
    
    static void recexp(struct state *state, size_t di, size_t si)
    {
        size_t start = si;
        size_t pos = si;
    
        for(; state->src[pos]; ++pos)
        {
            if(state->src[pos] == '\n') start = pos + 1;
            else if(state->src[pos] == '\t')
            {
                size_t str_len = pos - si;
                size_t tab_len = state->tab_size - (pos - start) % state->tab_size;
    
                recexp(state, di + str_len + tab_len, pos + 1);
                if(state->dest)
                {
                    memcpy(state->dest + di, state->src + si, str_len);
                    memset(state->dest + di + str_len, ' ', tab_len);
                }
    
                return;
            }
        }
    
        state->size = di + pos - si + 1;
        if(state->expand && !state->dest) state->dest = malloc(state->size);
        if(state->dest)
        {
            memcpy(state->dest + di, state->src + si, pos - si);
            state->dest[state->size - 1] = 0;
        }
    }
    
    size_t expand_tabs(char **dest, const char *src, size_t tab_size)
    {
        struct state state = { dest ? *dest : NULL, src, tab_size, 0, dest };
        recexp(&state, 0, 0);
        if(dest) *dest = state.dest;
        return state.size;
    }
    
    int main(void)
    {
        char *expansion = NULL; // must be `NULL` for automatic allocation
        size_t size = expand_tabs(&expansion,
            "spam\teggs\tfoo\tbar\nfoobar\tquux", 4);
        printf("expanded size: %lu\n", (unsigned long)size);
        puts(expansion);
    }
    
    #包括
    #包括
    #包括
    结构状态
    {
    char*dest;
    常量字符*src;
    尺寸标签尺寸;
    大小;
    _布尔展开;
    };
    静态void recexp(结构状态*state,大小di,大小si)
    {
    大小\u t开始=si;
    尺寸=si;
    对于(;状态->src[pos];++pos)
    {
    如果(state->src[pos]='\n')start=pos+1;
    否则如果(状态->src[pos]='\t')
    {
    尺寸=位置-si;
    大小选项卡长度=状态->选项卡大小-(pos-start)%state->选项卡大小;
    recexp(状态,di+str\U len+tab\U len,位置+1);
    如果(状态->目的地)
    {
    memcpy(state->dest+di,state->src+si,str_len);
    memset(state->dest+di+str_len',tab_len);
    }
    返回;
    }
    }
    状态->大小=di+位置-
    
    #include <stddef.h>
    #include <stdlib.h>
    #include <string.h>
    
    static char *expand_tabs(const char *s) {
      int i, j, extra_space;
      char *r, *result = NULL;
    
      for(i = 0; i < 2; ++i) {
        for(j = extra_space = 0; s[j]; ++j) {
          if (s[j] == '\t') {
            int es0 = 8 - (j + extra_space & 7);
            if (result != NULL) {
              strncpy(r, "        ", es0);
              r += es0;
            }
            extra_space += es0 - 1;
          } else if (result != NULL)
            *r++ = s[j];
        }
        if (result == NULL)
          if ((r = result = malloc(j + extra_space + 1)) == NULL)
            return NULL;
      }
      *r = 0;
      return result;
    }
    
    #include <stdio.h>
    
    int main(int ac, char **av) {
      char space[1000];
      while (fgets(space, sizeof space, stdin) != NULL) {
        char *s = expand_tabs(space);
        fputs(s, stdout);
        free(s);
      }
      return 0;
    }