如何在括号之间分开术语并将其列出



我有以下问题,我有一个带有x项的字符串,有些则在括号中。我需要能够将括号中的术语分开,然后将它们放在列表的节点中:

列表中的第一个节点包含整个字符串,然后括号中的术语存储在以下节点中。

这样的东西:字符串:a*(b+(c+j)-h)*2 + (a+(b-c))

so,在列表中:firts节点:

a*(b+(c+j)-h)*2+(a+(b-c))->(b+(c+j)-h)->(c+j)->(a+(b-c))->(b-c)

我需要想法来实施术语分离的算法,我坚持这一部分。我已经实现了所有列表部分。他们要求我的功能是

void separate (char * s)

递归是一种解析嵌套结构的常见方法,正如@SomeProgrammerDude所说的那样。

我要寻找的算法的直觉是:

1(递归调用单独的方法。

2(每个呼叫都应在括号中包含一个术语,然后将其插入列表的末尾。

3(该方法必须达到两种模式:在输入字符串上迭代并在填写术语时通过输入字符串进行迭代。

我们正在寻找开放括号的第一次出现。一旦找到此方法,该方法就可以启用"读取"模式,并开始将其路径中的所有内容复制到本地缓冲区,直到找到闭合括号为止。

发生这种情况时,该方法禁用"读取"模式,标记该术语现在准备在列表中插入。

4(如何绕过嵌套术语?找到第一个开口括号后,保持括号的计数器,根据括号的数量进行调整(开口括号的 1,-1用于闭合(。

如果满足了开放括号,则计数器会增加1。如果计数器大于1,则指示我们发现了一个嵌套的术语,因此,当满足其关闭括号时,我们不应停止而是继续,同时将计数器减少1。

5(一旦插入了一个项,我们应该递归地调用我们的单独方法,现在输入字符串应该是已经解析的输入,从我们找到的第一个打开括号之后的字符开始。

6(递归的基本案例?当所有输入被消耗时,我们到达了空字符串终结器。


完整代码示例(随着程序进行时显示状态的未点击打印语句(:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct listnode *Listptr;
struct listnode {
  char* value;
  Listptr next;
};
void insert_at_end(Listptr *, char *);
void print(Listptr);
void free_list(Listptr *);
void separate(Listptr *list, char *s)
{
  if(!*s)
    return;
  int parentheses = 0, read = -1, i = 0;
  char term[16] = {0}, *opening_parenthesis;
  //printf("Before while input is %sn", s);
  while(*s)
  {
    //printf("#parentheses = %d, read = %d, i = %d, term = %sn", parentheses, read, i, term);
    if(*s == '(')
    {
      if(parentheses == 0)
        opening_parenthesis = s;
      read = 1;
      parentheses++;
    }
    else if(*s == ')')
    {
      if(parentheses > 1)
        parentheses--;
      else if(parentheses == 1)
      {
        term[i++] = *s;
        read = 0;
      }
    }
    if(read == 1)
    {
      term[i++] = *s;
      //puts(term);
    }
    else if (read == 0)
    {
      insert_at_end(list, term);
      separate(list, opening_parenthesis + 1);
      return;
    }
    s++;
  }
}
int main(void)
{
  char buf[128];
  fgets(buf, sizeof(buf), stdin);
  // eat trailing newline of fgets
  if(!strlen(buf))
  {
    printf("No input, exiting..n");
    return EXIT_FAILURE;
  }
  // eat trailing newline of fgets
  buf[strlen(buf)] = ''; 
  // Goal: a*(b+(c+j)-h)*2+(a+(b-c))->(b+(c+j)-h)->(c+j)->(a+(b-c))->(b-c)
  Listptr list = NULL;
  insert_at_end(&list, buf);
  //printf("First node: "); print(list);
  separate(&list, buf);
  print(list);
  free_list(&list);
  return EXIT_SUCCESS;
}
void insert_at_end(Listptr *ptraddr, char* str)
{                    /* Insert str as last element of list *ptraddr */
  while (*ptraddr != NULL)                     /* Go to end of list */
    ptraddr = &((*ptraddr)->next);/* Prepare what we need to change */
          /* In real application, check for NULL pointer of malloc! */
  *ptraddr = malloc(sizeof(struct listnode)); /* Space for new node */
     /* Space for string. Length of str + 1 for the null terminator */
  (*ptraddr)->value = malloc((strlen(str) + 1) * sizeof(char));
  strcpy((*ptraddr)->value, str);                      /* Copy str  */
  (*ptraddr)->next = NULL;              /* There is no next element */
}
void print(Listptr list)                  /* Print elements of list */
{ 
  while (list != NULL) {       /* Visit list elements up to the end */
    printf("%s--> ", list->value);         /* Print current element */
    list = list->next;                        /* Go to next element */
  }
  printf("NULLn");                            /* Print end of list */
}
void free_list(Listptr *ptraddr)
{ 
  Listptr templist = *ptraddr;
  while(*ptraddr != NULL) {
      *ptraddr = (*ptraddr)->next;
      free(templist->value);
      free(templist);
      templist = *ptraddr;
  }
  *ptraddr = NULL; 
}

输出(输入(:

a*(b (c j(-h(*2 (a (b-c(( ->(b (c j(-h( ->(c j( ->(a (b-c(( ->(b-c( -> null

Note1:我更改了您单独的方法的原型,以允许将列表作为参数传递。我想在您的实施中,列表是一个全局变量之类的东西,通常应该避免。

note2:仅使用括号数量的变量来确定何时读取(何时呈阳性(,就可以在不使用读标志的情况下实现它,您可以分配保留值(-1例如(,指定何时完成阅读,并且该术语已准备就绪列表插入。

假设1:术语的长度最多是15个字符。代码通常应检查该长度是否没有超过 - 在我的实现中未完成。

ashaption2:假定术语有效,即它包含相同数量的开口和闭合括号。在现实世界中,应该对输入进行消毒。

live demo

ps:我也在strlist(c(中实现列表。

John Bollinger推荐:

大概是为此目的的递归功能将返回有关解析了多少字符串的信息,以便其呼叫者可以在字符串的其余部分(如果有(中捡起。这意味着它不一定是递归的((本身是单独的((本身,而是它调用的某些辅助功能。

op已经绘制了草图(在评论中(:

如果给出一个函数,它将返回一个带有两个括号内部的子字符串,我可以将其插入列表中。然后,我可以按照他们上面告诉我的递归来回电。

这是我开始的地方。

有多种构建子字符串的方法:

  1. 通过用定界符''覆盖字符(可能是暂时(&ndash;就像完成在strtok()

  2. 为子字符串分配内存,并复制原始字符串的部分(使用例如strdup()(

  3. 只需返回子弦的长度(并且可能是,开始(

我使用了第三个选项。

#include <assert.h>
#include <stdio.h>
#include <string.h>
void add_term(char *s, size_t len)
{
  printf("'%.*s'n", (int)len, s);
}
size_t find_term(char *s, size_t len)
{
  assert(*s == '(');
  int n = 1;
  size_t i = 1;
  for (; i < len && n; ++i) n += (s[i] == '(') - (s[i] == ')');
  return n ? fprintf(stderr, "ERROR!n"), 0 : i;
}
void separate_terms(char *s, size_t lenTotal, int addTotal)
{
  for (size_t i = 0; i < lenTotal;) {
    switch (s[i]) {
      case '(': {
        const size_t len = find_term(s + i, lenTotal - i);
        if (!len) return;
        if (len < lenTotal + addTotal) add_term(s + i, len);
        separate_terms(s + i + 1, len - 2, 1);
        i += len;
      } break;
      case ')': fprintf(stderr, "ERROR!n"); return;
      default: ++i;
    }
  }
}
void separate(char *s)
{
  size_t lenTotal = strlen(s);
  printf("Term: "); add_term(s, lenTotal);
  separate_terms(s, lenTotal, 0);
}
int main(void)
{
  // sample of OP:
  separate("a*(b+(c+j)-h)*2 + (a+(b-c))");
}

输出:

Term: 'a*(b+(c+j)-h)*2 + (a+(b-c))'
'(b+(c+j)-h)'
'(c+j)'
'(a+(b-c))'
'(b-c)'

注意:

op指出,已经解决了列表中的结果存储(具有适当的管理(。因此,我改用简单的控制台输出(在add_term()中(。

find_term()只是在计算开口和闭合括号,直到找到与第一个开口相对应的闭合括号(成功(或达到字符串末端(错误情况(。

separate_terms()是递归的下降扫描仪,以在一项术语中查找子字体。

它迭代输入字符串的字符。每次找到开口括号(((时,都会调用find_term()来确定子术语的长度并添加结果。通过递归地将自己自称为限制的长度递归地分析。之后,由于现在对其进行了完全分析,因此跳过了子术语的长度。

在摆弄边缘案例时,我发现我的初始版本(明显较短(的版本报告了当完整的输入项在括号中(例如(a + b)(时重复的项。因此,我添加了一个标志addTotal来表示如果输入总长度,是否应添加发现的子术语。

separate()很短。这只是一个包装器,添加一个术语以进行完整输入并执行递归separate_terms()的高级调用。(0用于addTotal,以防止重复添加完整输入。(

我制作了其他一些样本,我认为这些样本是我的解决方案的边缘案例,以检查潜在的错误:

  // valid edge cases:
  separate(""); // valid?
  separate("()"); // valid?
  separate("a");
  separate("(a + b)");
  separate("(a + b) * 2");
  separate("(a + b) * (c - d)");
  separate("((a + b))");
  // invalid cases:
  separate("(");
  separate("((");
  separate(")");
  separate("))");
  separate("())(()");
  separate("a(a(a");
  separate("((a)))");

输出:

Term: ''
Term: '()'
Term: 'a'
Term: '(a + b)'
Term: '(a + b) * 2'
'(a + b)'
Term: '(a + b) * (c - d)'
'(a + b)'
'(c - d)'
Term: '((a + b))'
'(a + b)'
Term: '('
ERROR!
Term: '(('
ERROR!
Term: ')'
ERROR!
Term: '))'
ERROR!
Term: '())(()'
'()'
ERROR!
Term: 'a(a(a'
ERROR!
Term: '((a)))'
'((a))'
'(a)'
ERROR!

coliru

实时演示

相关内容

  • 没有找到相关文章

最新更新