Объявление строки в действии, соответствующем шаблону, влияет на следующее действие, соответствующее шаблону - PullRequest
0 голосов
/ 07 апреля 2020

Я извлекаю имя тега из входного файла при сопоставлении с шаблоном "<"([a-z][a-z]*), затем извлекаю подстроку, содержащую только часть имени в строке (массив символов) tagName, используя функцию strncpy.

Большинство работает хорошо, когда сопоставляется первый <div, за исключением того, что длина tagName, которая является извлеченной подстрокой, полученной в strncpy(tagName, yytext + 1, yytextlen - 1);, должна быть 3 для div , но получить 6 вместо этого.

Во время второго совпадения ввода <a значение tagName во время первого совпадения сохраняется здесь и влияет на вывод в действии.

Я не могу понять причину этого поведение.

Ожидаемый выход:

tagName: 0\y�� /* Garbage value */, tagName length: 6 /* Why is this 6 */
An html tag { yytext: <a, yyleng: 2, text: a, len: 1 }

Токовый выход : см. раздел output ниже.

Я использую flex версию 2.6.0 и компилятор g cc в Ubuntu 16.


lex-файл:

HTML_TAG    [a-z][a-z]*                                                                                              
REACT_TAG   [A-Z][[:alnum:]]*

%%

"<"{HTML_TAG}   {
  int yytextlen = yyleng;
  char tagName[yytextlen - 1];
  printf("tagName: %s, tagName length: %lu\n", tagName, strlen(tagName));
  strncpy(tagName, yytext + 1, yytextlen - 1);                                                                       
  printf("An html tag { yytext: %s, yyleng: %zu, text: %s, len: %lu }\n", yytext, yyleng, tagName, strlen(tagName)); 
  printf("\n\n");
} 

"<"{REACT_TAG}  {
                      printf("A react tag { text: %s, len: %zu }\n", yytext, yyleng);
                      printf("\n\n");
}                     

"</"{HTML_TAG}  {                                                                                                    
                      printf("A closing html tag { text: %s, len: %zu }\n", yytext, yyleng);                        
                      printf("\n\n");                                                                                
}                     

"</"{REACT_TAG} {                                                                                                    
                      printf("A closing react tag { text: %s, len: %zu }\n", yytext, yyleng);                       
                      printf("\n\n");                                                                                
}                     

[ \t\n] /* eat up whitespace */                                                                                      

.   ;

%%  

int main(int argc, char **argv)                                                                                      
{
  ++argv, --argc; /* skip over program name */                                                                       
  if (argc > 0)
    yyin = fopen(argv[0], "r");
  else
    yyin = stdin;

  yylex(); 
} 

Входной файл:

<div
   attribute1=""
   attribute2=""
   attribute3=""
>
    <a>
        Text Content
    </a>
    <ReactTag attribute4="" />
    <ReactTag2 attribute5="">
        Text Content
    </ReactTag2>
</div>

Выход:

tagName: 0\y��, tagName length: 6
An html tag { yytext: <div, yyleng: 4, text: div��, len: 6 }

tagName: div��, tagName length: 6
An html tag { yytext: <a, yyleng: 2, text: aiv��, len: 6 }

An closing html tag { text: </a, len: 3 }


A react tag { text: <ReactTag, len: 9 }


A react tag { text: <ReactTag2, len: 10 }


An closing react tag { text: </ReactTag2, len: 11 }


An closing html tag { text: </div, len: 5 }

1 Ответ

2 голосов
/ 07 апреля 2020

Я смотрел только на указанное вами действие. Если вы сделаете те же ошибки в другом месте, я уверен, что вы сможете их найти.

Вот действие "<"{HTML_TAG} с моими комментариями:

{
  int yytextlen = yyleng;

Какой смысл этой переменной? yyleng не собирается менять свое значение во время выполнения этого действия. Просто используйте его.

  char tagName[yytextlen - 1];

Вы хотите сохранить тэг, чьи символы yyleng - 1 (так как yyleng включает <.) Это означает, что вам нужна временная строка, размер которой yyleng - 1 + 1 (или yyleng для краткости), потому что вам нужно NUL-завершить копию. За исключением того, что вам действительно не нужна эта копия. Но мы вернемся к этому позже.

  printf("tagName: %s, tagName length: %lu\n", tagName, strlen(tagName));

Я знаю, что вы планируете скопировать yytext в tagName, но вы еще этого не сделали. Так что на данный момент это неинициализированное хранилище. Попытка напечатать это неопределенное поведение. Попытка получить его длину с strlen - Неопределенное Поведение. Очевидно, это собирается напечатать мусор. (В любом случае, зачем вам вычислять strlen? Вы точно знаете, как долго эта строка будет: yyleng - 1.)

  strncpy(tagName, yytext + 1, yytextlen - 1);

В какой-то момент я ' Я собираюсь отказаться от этого аргумента, но это отличная иллюстрация того, почему вы не должны использовать strncpy (за исключением редкого случая использования, для которого он был разработан: поля базы данных фиксированной длины, которые не требуют завершения NUL) , Кажется, люди думают, что strncpy "безопаснее", чем strcpy, потому что в его имени есть n. Но на самом деле это крайне небезопасно, даже менее безопасно, чем strcpy, потому что не завершает NUL-копию . Как мы видели выше, вы также не оставили достаточно места для терминатора NUL, поэтому, если бы вы использовали strcpy, который завершает NUL, то он записал бы терминатор NUL вне буфера. Но если бы вы сделали буфер достаточно большим, strcpy было бы совершенно правильно.

Кроме того, в случае, когда исходная строка в strncpy короче цели, strncpy заполняет остальную часть цели значениями NUL. Все это. Это почти всегда пустая трата циклов (за исключением случаев, подобных этому, когда он вообще не записывает NUL и выдает неопределенное поведение).

Если вы действительно хотите сделать копию строки, ограниченную максимальной длиной , используйте strndup (если она есть в вашей библиотеке C, что большинство делает в наши дни). strndup копирует строку с ограничением длины. И это NUL-прекращает копирование. И динамически распределяет достаточно места для копии. Если вам нужен безопасный интерфейс, используйте его.

Но почему вы чувствуете, что вам нужно сделать копию здесь? Если вы планируете передать токен парсеру, то вам действительно понадобится копия, но локальный массив переменной длины не является той копией, которая вам понадобится, поскольку время жизни локального массива заканчивается, как только действие завершается, что задолго до того, как копия может быть использована. Если вам нужна копия, вам понадобится динамически распределяемая копия. И это именно то, что strndup дал бы вам.

  printf("An html tag { yytext: %s, yyleng: %zu, text: %s, len: %lu }\n",
                        yytext, yyleng, tagName, strlen(tagName));

Итак, теперь вы сделали свою копию. Но вы сделали это с помощью библиотечной функции, которая не завершает NUL, так что все еще остается неопределенным поведение использовать копию, как если бы она была строкой. Это была бы только строка, если бы она была завершена NUL. И все еще не определено поведение - передавать копию в strlen, как если бы она была строкой.

С другой стороны, распечатка yytext и yyleng в порядке.

  printf("\n\n");
}

На этом действие заканчивается. tagName больше не существует. Его жизнь подошла к концу. yytext все еще в порядке, но ненадолго: как только сканер начинает искать следующий токен (который сразу же, так как ваше действие не return токен для его вызывающего), он вернет контроль над yytext, изменяя его содержимое непредсказуемым образом. Поэтому, если бы вам потребовалась копия строки для возврата с типом токена, вы должны были бы уже сделать копию, которая была еще жива.

Надеюсь, что все поможет.

...