Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > удалить комментарии из исходников


Автор: inside_pointer 4.4.2008, 02:50
Как найти комментарий в строке, не используя библиотечных функций языка C ?

Код


    /* этот */

/* этот */

char str[] = "    /* это не трогать */     ";

/*
 *    вот этот вот,
 *    такой вот,
 *    длинный
 */



Можно использовать переменных две штуки, и строки две штуки.

Автор: Mayk 4.4.2008, 05:50
Цитата(inside_pointer @  4.4.2008,  06:50 Найти цитируемый пост)

Можно использовать переменных две штуки, и строки две штуки.

А можно не страдать фигнёй и использовать либы предоставляющи http://www.perl.com/doc/FAQs/FAQ/oldfaq-html/Q4.27.html или хотя бы тупо прогнать через http://www.lysator.liu.se/c/ANSI-C-grammar-l.html?

Автор: 0lmer 4.4.2008, 06:13
Цитата(Mayk @  4.4.2008,  05:50 Найти цитируемый пост)
А можно не страдать фигнёй и использовать либы предоставляющи perl-овые regexp'ы или хотя бы тупо прогнать через lex?


Мож это какое нить учебное задание и злой препод строго настрого запретил пользоваться либами?

Автор: inside_pointer 4.4.2008, 06:24
Это всё ещё неизвестно

Код


copy(search, line);
                
        reverse(search);
            clearline(search);
                if (search[0] == '/' && search[1] == '*') {
                    if (close == OFF)
                        close = ON;
                reverse(search);
                    clearline(search);
                        if (search[0] == '/' && search[1] == '*')
                            if (open == OFF)
                                open = ON;
                }            
        

if ((open == OFF && close == OFF) || (open == ON && close == ON))



Тут функции, которые пытаюсь использовать
Код

/* copy: копирует строку 'from' в 'to'; длина to считается достаточной */
void copy(char to[], char from[])
{
    int i;
    
    i = 0;
    while ((to[i] = from[i]) != '\0')
        ++i;
}

/* clearline: стирает лишние пробельные символы в конце строки */
int clearline(char s[])
{
    int c, i;
    
    for (i = 0; s[i] != '\n'; ++i)
        ;
    while (s[i] == '\n' || s[i] == ' ' || s[i] == '\t')
        if (i > 0)
            --i;
        else
            return 1;
    
    s[++i] = '\n';
    s[++i] = '\0';
    return i;
}

/* reverse: считывает строку в s, обращает её */
void reverse(char s[])
{
    int i, step, save;
    for (i = 0; s[i] != '\0'; ++i)
        ;
    if (s[i-1] == '\n')
        --i;
    for (step = 0; step < i / 2; ++step) {
        save = s[step];
        s[step] = s[i-1-step];
        s[i-1-step] = save;
    }
}


Пробовал через c = getchar() и т.д., но получилась такая громадина и чем дальше, тем хуже, особенно когда надо двойные кавычки учесть.

Тут в результате нужно получить программку в теле понятный блок функций чо-то делает со строкой, и сами функции простые как 2х2, накидал чо-то вроде получается с флажками открытия и закрытия комментария, но чувствуется что это процентов 30% от того что надо наваять.

Автор: xvr 4.4.2008, 10:54
Цитата(inside_pointer @ 4.4.2008,  02:50)
Как найти комментарий в строке, не используя библиотечных функций языка C ?

Код


    /* этот */

/* этот */

char str[] = "    /* это не трогать */     ";

/*
 *    вот этот вот,
 *    такой вот,
 *    длинный
 */



Можно использовать переменных две штуки, и строки две штуки.

Хватит и одной переменной, дополнительных строк вообще не надо.
Делаешь State Machine, переменная обозначает текущее состояние:
Normal - вне строк и коментариев
String - внутри строки ("")
Char - внутри строки ('')
Comment - внутри комментария /* .. */
OneLineComment - внутри комментария //
Далее читаешь входной поток по лексемам (что бы определить /* */ //) и меняешь состояние в зависимости от текущего и прочтенной лексемы, если текущее состояние не Comment/OneLineComment - выводишь прочтенную лексему в выходной поток.
Сочетание \<любой символ> в состояниях String и Char считаются 2мя обычными символами

Автор: inside_pointer 4.4.2008, 11:27
Цитата(xvr)

Далее читаешь входной поток по лексемам 

А это что значит ?

Лексема эта должна понимать знак EOF, кто такая лексема и как не дать ей вылезти за край файла ?

Автор: Mayk 4.4.2008, 12:24
Цитата(xvr @  4.4.2008,  14:54 Найти цитируемый пост)

Хватит и одной переменной, дополнительных строк вообще не надо.
Делаешь State Machine, переменная обозначает текущее состояние:

только зачем лексер делать руками когда есть lex и готовый ansi-c.l? 

Автор: Alek86 4.4.2008, 12:45
Mayk
Цитата(inside_pointer @  4.4.2008,  02:50 Найти цитируемый пост)
 не используя библиотечных функций языка C ?

думаю, имеется в виду самому написать


кстати, а по стандарту как компилер должен убрать комменты в таком случае:
это:
Код
111 /* /* 222 */ */ 333

заменить на это:
Код
111 */ 333

или это:
Код
111  333

?

Автор: Mayk 4.4.2008, 12:48
Цитата(Alek86 @  4.4.2008,  16:45 Найти цитируемый пост)

думаю, имеется в виду самому написать

Трата времени.

Цитата(Alek86 @  4.4.2008,  16:45 Найти цитируемый пост)

кстати, а по стандарту как компилер должен убрать комменты в таком случае:
это:
Выделить всёкод C++
1:
    
111 /* /* 222 */ */ 333

заменить на это:
Выделить всёкод C++
1:
    
111 */ 333

ага. комментарии в си не являются вложенными. 

Автор: inside_pointer 4.4.2008, 12:51
Цитата(Mayk)

только зачем лексер делать руками когда есть lex и готовый ansi-c.l?  

Так есть string.h, я бы сделал через неё, надо понять устройство одной из таких функций на примере. И string.h неизвестно и лексер этот твой.

Автор: korian 4.4.2008, 13:16
Цитата(inside_pointer @  4.4.2008,  01:50 Найти цитируемый пост)
Можно использовать переменных две штуки, и строки две штуки.

ограничения не понял...
поэтоу... так правильно?
Код

void commentEraser(char* string)
{
    for (char* curchar = string; curchar[0] != 0; ++curchar)
    {
        if (curchar[0] == '/' && curchar[1] == '*')
        {
            for (curchar += 2; curchar[0] != 0 && (curchar[0] != '*' || curchar[1] != '/'); ++curchar) ;
            if (curchar[0] == 0)
                throw "bad comment";
            ++curchar;
            continue;
        }
        if (curchar[0] == '/' && curchar[1] == '/')
        {
            for (curchar += 2; curchar[0] != 0 && curchar[0] != '\n'; ++curchar) ;
            if (curchar[0] == 0)
                break;
        }
        if (curchar[0] == '"')
        {
            *string++ = *curchar;
            for (++curchar; curchar[0] != 0 && curchar[0] != '"'; ++curchar)
                *string++ = *curchar;
            *string++ = *curchar;

            if (curchar[0] == 0)
                throw "bad string";
            continue;
        }
        *string++ = *curchar;
    }
    *string = 0;
}


Автор: Alek86 4.4.2008, 13:28
Цитата(Mayk @  4.4.2008,  12:48 Найти цитируемый пост)
 комментарии в си не являются вложенными. 

в c++ то же самое?

Автор: Mayk 4.4.2008, 13:50
korian,  почти так. надо ещё так чтоб в 
Код

const char* str =" \" /* */";

/**/ не удалялись. 


в готовых грамматиках подобные тонкости уже расписаны кстати
 

Цитата(Alek86 @  4.4.2008,  17:28 Найти цитируемый пост)
в c++ то же самое? 

да.  многострочные комментарии не могут быть вложенными

Автор: inside_pointer 4.4.2008, 13:56
korian, лексема это типа две перменные типа ?

cl и cr

Код

for (i = 0; (c = line[i]) != '\0'; ++i)
    if ((cl = line[j]) == '/' && (cr = line[j+1]) == '*')
        выставить состояние флага, типа вход в комментарий произошёл


Суть в том, чтобы он комментарий вырезал только /*...*/ а не //...
И чтобы /*... /*...*/...*/ считалось одним комментарием.
Но чтобы эти штуки в строке типа a = "abcd \' \" /* ... /* ...  */  ...  */abcd \* \" ";
не были затронуты.

Я попробую сделать как предложил xvr, типа несколько состояний, и лексемы вида две переменные в одной левый символ в другой правый.

Автор: korian 4.4.2008, 14:08
Цитата(Mayk @  4.4.2008,  12:50 Найти цитируемый пост)
/**/ не удалялись. 

в задании этого не сказано  smile 
а так, конечно, чтобы все учесть, надо на лексемы разбивать и работать уже с ними, а не с символами.

Добавлено через 1 минуту и 46 секунд
Цитата(inside_pointer @  4.4.2008,  12:56 Найти цитируемый пост)
И чтобы /*... /*...*/...*/ считалось одним комментарием.

ну тогда сначала опишите полностью ваш стандарт комментов...
в обычном C/C++ из той строки только это коментарий - /*... /*...*/

Добавлено через 6 минут и 37 секунд
лексема - это, так сказать, символ, которым можно оперировать в языке.
\n - два символа, но одна лексема.

на более высоком уровне:
char* a = "string"
тут 4 лексемы:
тип char*
имя переменной a
оператор =
строка "string"

Автор: inside_pointer 4.4.2008, 15:13
То есть лексемы получаются когда строка делится по какому-то символу и в каждой лексеме проводится поиск нужных комбинаций, это то есть делается цикл который обрабатывает каждую лексему, во вроде то что нужно, этого я не знал.
Спасибо, korian, за разъяснения, терь вижу алгоритм.

Автор: inside_pointer 5.4.2008, 03:35
Вот короче получилось.

Код


#include <stdio.h>

#define IN  1   /* внутри комментария или кавычек */
#define OUT 0   /* снаружи комментария или кавычек */

/* удаляет комментарии такого вида из программы на C */
main()
{
    int c, i, save, kstate, cstate;
    
    kstate = cstate = OUT;
    while ((c = getchar()) != EOF) {
        
        if (c == '\\') {
            putchar(c);
            c = getchar();
            putchar(c);
            c = getchar();
        }
            
        if (c == '\"')
            if (kstate == OUT)    
                kstate = IN;
            else
                kstate = OUT;
                
        if (kstate == OUT) {                
            if (c == '/') {
                save = c;
                if ((c = getchar()) == '*')
                    cstate = IN;
                else if (c == '/')
                    putchar(c);
                else
                    c = save;
            }
            else if (c == '*')
                if ((c = getchar()) == '/') {
                    cstate = OUT;
                    c = getchar();
                }
        }        
                    
        if (cstate == OUT)            
            putchar(c);
    
    }
    

    return 0;

}

//


Автор: xvr 5.4.2008, 21:57
Цитата(inside_pointer @ 5.4.2008,  03:35)
Вот короче получилось.

Код


#include <stdio.h>

#define IN  1   /* внутри комментария или кавычек */
#define OUT 0   /* снаружи комментария или кавычек */

/* удаляет комментарии такого вида из программы на C */
main()
{
    int c, i, save, kstate, cstate;
    
    kstate = cstate = OUT;
    while ((c = getchar()) != EOF) {
        
        if (c == '\\') {
            putchar(c);
            c = getchar();
            putchar(c);
            c = getchar();
        }
            
        if (c == '\"')
            if (kstate == OUT)    
                kstate = IN;
            else
                kstate = OUT;
                
        if (kstate == OUT) {                
            if (c == '/') {
                save = c;
                if ((c = getchar()) == '*')
                    cstate = IN;
                else if (c == '/')
                    putchar(c);
                else
                    c = save;
            }
            else if (c == '*')
                if ((c = getchar()) == '/') {
                    cstate = OUT;
                    c = getchar();
                }
        }        
                    
        if (cstate == OUT)            
            putchar(c);
    
    }
    

    return 0;

}

//


Неправильно получилось:
  •  Символы, предваренные \ попадут в выходной поток, даже если они содержатся в комментариях
  •  Комбинация // попадет в выходной поток, даже если будет внутри коментария, как символ /
  •  Комбинация /<не *> попадет в выходной поток в виде /
  •  Просто * не попадет в выходной поток никогда
Теперь несколько советов:
  •  Чтение (и распознавание) лексемы рекомендуется делать в одном месте, а не размазывать его по всей процедуре - очень сложно убедится в правильности такого чтения и практически невозможно поддерживать такую правильность если приходится модифицировать код
  •  Конечный автомат, если в нем более 2х состояний и более 2х входных переменных рекомендуется кодировать в виде таблицы (или switch'а), иначе очень трудно разобраться в таком автомате
  •  КРАЙНЕ НЕ РОКЕМЕНДУЕТСЯ делать конечные автоматы с 2мя и более переменными состояния - их практически невозможно описать в каком либо формальном виде, собственно переходы между состояниями получаются крайне запутанными

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)