You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Lex中yytext累积单词问题:词频统计程序异常求助

问题:单词累积导致符号表匹配失效

我编写了一个读取单个或多个文件、统计单词出现次数及位置的程序,目前遇到yytext累积单词的问题。预期效果应为类似cat line 5 line 4 file 1的形式,但程序甚至无法进入用于比较符号表中已有单词与当前单词的if条件。实际出现单词累积的情况:比如"cat owns"本应是cat和owns分别存入不同节点,使用strcmp进行比较,但现在却是用"owns"与"cat owns"进行比较。

程序代码

%option noyywrap
%option nodefault
%option yylineno
%option warn
%option case-insensitive

%{
  #include <stdlib.h>
  #include <stdio.h>
  #include <string.h>

  typedef struct _la{      /* no da lista de ocorrencias */
    int line;
    char * arq;
    struct _la * next;
  } LineArq;

  typedef struct _node {    /* entrada na tabela de simbolos */
   char * word;
   LineArq *lines;
   struct _node * next;
  } Node;


  char *curfilename;

  Node * tabela = NULL;

  Node * getNodeL( char * word, int line, char * arq );
  void insertNode(char * word, int line, char * arq, Node **l);
  void show(Node *l);
%}

%%

a |
an |
and |
are |
as |
at |
be |
but |
for |
in |
is |
it |
of |
on |
or |
that |
the |
this |
to               { /* printf("DEBUG: [%d, %s] %s\n", yylineno, curfilename, yytext ); */ }  /* ESSES PADROES SAO IGNORADOS... */

[a-z]+(\'(s|t))?     { insertNode(yytext, yylineno, curfilename, &tabela);
                    /* printf("DEBUG: [%d, %s] %s\n", yylineno, curfilename, yytext ); */
                 }

.|\n             { /* printf("DEBUG: [%d, %s] %s\n", yylineno, curfilename, yytext ); */ }  /* IGNORA NOVA LINHA E QUALQUER OUTRO CARACTER... */

%%

int main(int argc, char *argv[]){
  int i = 0;

  for(i = 1; i < argc; i++) {
    FILE *f = fopen(argv[i], "r");
    if(!f) {
      perror(argv[i]);
      return (1);
    }
    curfilename = argv[i]; 
    yyrestart(f);
    yylineno = 1;
    yylex();
    fclose(f);
  }

  show(tabela);
}

Node * getNodeL(char * word, int line, char * arq){
  
  Node * tmp = (Node *) malloc(sizeof(Node));

  if ( tmp == NULL ) {
    printf("Erro ao alocar memoria para o no da lista\n");
    exit( 1023 ); /* documentar no Dicionario de dados */
  }

  LineArq * la = (LineArq *) malloc(sizeof(LineArq));

  if (la == NULL){
    printf("Erro ao alocar memoria para o no da lista\n");
    exit( 1024 ); /* documentar no Dicionario de dados */
  }

  la->line = line;
  la->arq = arq;
  la->next = NULL;
  
  tmp->word = word;
  tmp->lines = la;
  tmp->next = NULL;

  return tmp;
}

void insertNode(char * word, int line, char * arq, Node **l){


  printf("[DEBUG] >>>>>>>> %s     %d 	 %s\n", word, line, arq);

  Node * p = *l;
  int count = 0;
  
  /* primeira entrada na tabela */
  if ( p == NULL ) {
    printf("entrou\n");
    *l = getNodeL(word, line, arq);
    return;
  }
  printf("%s\n", p->word);

  while( p != NULL ) {
     printf("[DEBUG]       [%s] == [%s]\n", word, p->word);
     if ( strcmp(word, p->word) == 0 ) {
        printf("entrou palavra igual\n");
        break;
     }
     p = p-> next;
  }

  if ( p != NULL ) { /* palavra encontrada na tabela */
      LineArq * la = (LineArq *) malloc(sizeof(LineArq));
      la->line = line;
      la->arq = arq;
      p->lines->next = la;
      la->next = NULL;
  } else {
    Node * new = getNodeL(word, line, arq);
    new->next = *l;
    *l = new;
  }
}

void show(Node *l){
  Node * tmp = l;
  LineArq * linesA;
  while( tmp != NULL ) {
    printf("Palavra: %10s\n", tmp->word);
    linesA = tmp->lines;
    while (linesA != NULL){
      printf("	Linha: %d 	 Arquivo: %s\n", linesA->line, linesA->arq);
      linesA = linesA->next;
    }
    /*
    if ( tmp->next != NULL ) {
        printf(", ");
    }
    */
    tmp = tmp->next;
  }
}

程序输出结果

because the cat prefers]
[DEBUG] >>>>>>>> colder 3 teste.txt
prefers colder
[DEBUG] [colder] == [prefers colder]
[DEBUG] [colder] == [cat prefers colder]
[DEBUG] [colder] == [because the cat prefers colder]
[DEBUG] [colder] == [water]
because the cat prefers colder]
[DEBUG] [colder] = [warm water
because the cat prefers colder]
[DEBUG] [colder] == [drink warm water
because the cat prefers colder]
[DEBUG] [colder] == [like to drink warm water
because the cat prefers colder]
[DEBUG] [colder] = [doesn't like to drink warm water]
because the cat prefers colder]
[DEBUG] [colder] = [cat doesn't like to drink warm water]
because the cat prefers colder]
[DEBUG] [colder] == [owner
The cat doesn't like to drink warm water
because the cat prefers colder]
[DEBUG] [colder] = [its owner
The cat doesn't like to drink warm water
because the cat prefers colder]
[DEBUG] [colder] = [own's its owner
The cat doesn't like to drink warm water
because the cat prefers colder]
[DEBUG] [colder] = [cat own's it’s owner

问题分析与修复

核心问题

你直接将Flex的yytext指针赋值给结构体的word字段,但yytext是Flex内部输入缓冲区的临时指针,后续扫描操作会修改这个缓冲区的内容(包括扩容、覆盖原有数据),导致之前保存的节点里的word指向的内容被篡改,出现单词累积、拼接的异常情况。

修复方法

必须为每个单词分配独立的内存,保存yytext的副本,而不是直接复用指针。修改getNodeL函数中的单词赋值逻辑:

Node * getNodeL(char * word, int line, char * arq){
  
  Node * tmp = (Node *) malloc(sizeof(Node));
  if ( tmp == NULL ) {
    printf("Erro ao alocar memoria para o no da lista\n");
    exit( 1023 );
  }

  LineArq * la = (LineArq *) malloc(sizeof(LineArq));
  if (la == NULL){
    printf("Erro ao alocar memoria para o no da lista\n");
    exit( 1024 );
  }

  la->line = line;
  la->arq = arq; // 此处curfilename指向argv参数,程序运行期间有效,若需更安全可改为strdup(arq)
  la->next = NULL;
  
  // 复制单词到独立内存
  tmp->word = strdup(word);
  if (tmp->word == NULL) {
    printf("Erro ao alocar memoria para a palavra\n");
    free(tmp);
    free(la);
    exit(1025);
  }
  tmp->lines = la;
  tmp->next = NULL;

  return tmp;
}

额外优化(可选)

为避免内存泄漏,可添加一个释放符号表内存的函数,并在程序结束时调用:

void freeTable(Node **l) {
  Node *tmp = *l;
  while (tmp != NULL) {
    Node *nextNode = tmp->next;
    // 释放行号与文件链表
    LineArq *laTmp = tmp->lines;
    while (laTmp != NULL) {
      LineArq *nextLa = laTmp->next;
      // 如果la->arq用了strdup,此处需添加free(laTmp->arq);
      free(laTmp);
      laTmp = nextLa;
    }
    // 释放单词内存
    free(tmp->word);
    free(tmp);
    tmp = nextNode;
  }
  *l = NULL;
}

在main函数的show(tabela);之后添加:

freeTable(&tabela);

内容的提问来源于stack exchange,提问作者PRYSCILLA ROMÃO DE MEDEIROS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 06:45:36