2014-01-19 3 views
1

я не могу показаться, чтобы получить это право, перепробовали все, но ..Количество символов комментариев в файле (программирование C)

int commentChars() { 
char str[256], fileName[256]; 
FILE *fp; 
int i; 


do{ 
    long commentCount=0; 
    fflush(stdin); 
    printf("%s\nEnter the name of the file in %s/", p, dir); 
    gets(fileName); 

    if(!(fp=fopen(fileName, "r"))) { 
      printf("Error! File not found, try again"); 
       return 0; 
    } 

    while(!feof(fp)) { 
      fgets(str,sizeof str,fp); 
      for(int i=0;i<=sizeof str;i++) { 
       if(str[i] == '/' && str[i+1] == '/') { 
         commentCount += (strlen(str)-2); 
       } 
      } 
    } 

    fclose(fp); 

     printf("All the chars, contained in a comment: %ld\n", commentCount); 
     puts(p); 
     printf("Do you want to search for another file?<Y/N>: "); 
     i=checker(); 


}while(i);} 

В результате получается «Все символы, containted в комментарий: 0 ", хотя у меня есть комментарии. И мой второй вопрос был. Аналогично, как я могу сделать то же самое для комментариев, содержащих/* * /, кажется для меня невозможной работой.

+0

Может быть работа для [гибкого] (Http: //flex.sourceforge.net /) ... –

+0

Вы используете очень опасный код. Вы должны держаться подальше от 'fflush (stdin)' и 'gets()'. – edmz

+1

Обратите внимание, что выполнять работу очень сложно. Основы не так уж плохи, но вам нужно знать о символах обратной косой черты, указывающих на сращивание строк, и о символьных константах (''/* ''- это не начало комментария, это многосимвольная константа) и строки (''/* это не комментарий */"' - [ceci n'est pas une pipe] (https://en.wikipedia.org/wiki/The_Treachery_of_Images) и Magritte?). Триграфы также являются (теоретической) проблемой. –

ответ

1

Я думаю, что вам лучше всего использовать регулярные выражения. Они кажутся страшными, но они действительно не так уж плохи для таких вещей. Вы всегда можете попробовать поиграть в гольф регулярок на практике ;-)

я бы подойти к его следующим образом:

  • Построить регулярное выражение, которое улавливает комментарии
  • Сканирование файла для него
  • графа персонажи в матче

Используя некоторые regex code и немного о том, соответствие comments in C, я взломал это вместе, что позволит вам рассчитывать аль l байты, которые являются частью комментария стиля блока/* */- Включая разделители. Я только тестировал его на OS X. Я полагаю, вы можете справиться с остальными?

#include <regex.h> 
#include <stdio.h> 
#include <stdlib.h> 

#define MAX_ERROR_MSG 0x1000 

int compile_regex(regex_t *r, char * regex_text) 
{ 
    int status = regcomp (r, regex_text, REG_EXTENDED|REG_NEWLINE|REG_ENHANCED); 
    if (status != 0) { 
     char error_message[MAX_ERROR_MSG]; 
     regerror (status, r, error_message, MAX_ERROR_MSG); 
     printf ("Regex error compiling '%s': %s\n", 
      regex_text, error_message); 
     return 1; 
    } 
    return 0; 
} 
int match_regex(regex_t *r, const char * to_match, long long *nbytes) 
{ 
    /* Pointer to end of previous match */ 
    const char *p = to_match; 
    /* Maximum number of matches */ 
    size_t n_matches = 10; 
    /* Array of matches */ 
    regmatch_t m[n_matches]; 

    while(1) { 
     int i = 0; 
     int nomatch = regexec (r, p, n_matches, m, 0); 
     if(nomatch) { 
      printf("No more matches.\n"); 
      return nomatch; 
     } 
     //Just handle first match (the entire match), don't care 
     //about groups 
     int start; 
     int finish; 
     start = m[0].rm_so + (p - to_match); 
     finish = m[0].rm_eo + (p - to_match); 
     *nbytes += m[0].rm_eo - m[0].rm_so; 

     printf("match length(bytes) : %lld\n", m[0].rm_eo - m[0].rm_so); 
     printf("Match: %.*s\n\n", finish - start, to_match + start); 
     p += m[0].rm_eo; 
    } 
    return 0; 
} 

int main(int argc, char *argv[]) 
{ 
    regex_t r; 
    char regex_text[128] = "/\\*(.|[\r\n])*?\\*/"; 
    long long comment_bytes = 0; 

    char *file_contents; 
    size_t input_file_size; 
    FILE *input_file; 
    if(argc != 2) { 
     printf("Usage : %s <filename>", argv[0]); 
     return 0; 
    } 
    input_file = fopen(argv[1], "rb"); 
    fseek(input_file, 0, SEEK_END); 
    input_file_size = ftell(input_file); 
    rewind(input_file); 
    file_contents = malloc(input_file_size * (sizeof(char))); 
    fread(file_contents, sizeof(char), input_file_size, input_file); 

    compile_regex(&r, regex_text); 
    match_regex(&r, file_contents, &comment_bytes); 
    regfree(&r); 
    printf("Found %lld bytes in comments\n", comment_bytes); 

    return 0; 
} 
1

Эта принципиальная тривиальная модификация вашего кода касается нескольких проблем в вашем коде.

  1. Вы не должны использовать feof() вот так - `while (!feof(file)) is always wrong.
  2. Вы не должны читать данные, которые не являются частью прочитанной строки.

Я также отредактировал ваш код так, что функция принимает имя файла, открывает, подсчитывает и закрывает его, а также сообщает о найденном.

#include <stdio.h> 
#include <string.h> 

// Revised interface - process a given file name, reporting 
static void commentChars(char const *file) 
{ 
    char str[256]; 
    FILE *fp; 
    long commentCount = 0; 

    if (!(fp = fopen(file, "r"))) 
    { 
     fprintf(stderr, "Error! File %s not found\n", file); 
     return; 
    } 

    while (fgets(str, sizeof(str), fp) != 0) 
    { 
     int len = strlen(str); 
     for (int i = 0; i <= len; i++) 
     { 
      if (str[i] == '/' && str[i + 1] == '/') 
      { 
       commentCount += (strlen(str) - 2); 
       break; 
      } 
     } 
    } 

    fclose(fp); 

    printf("%s: Number of characters contained in comments: %ld\n", file, commentCount); 
} 

int main(int argc, char **argv) 
{ 
    if (argc == 1) 
     commentChars("/dev/stdin"); 
    else 
    { 
     for (int i = 1; i < argc; i++) 
      commentChars(argv[i]); 
    } 
    return 0; 
} 

При запуске на исходном коде (ccc.c), он дает:

ccc.c: Number of characters contained in comments: 58 

Комментарий не очень полный (ой), но он служит, чтобы показать, что происходит. Он подсчитывает новую строку, которую fgets() сохраняет как часть комментария, хотя инсталлятор // не учитывается.

Работа с /* комментариев сложнее. Вам нужно заметить косую черту, за которой следует звезда, а затем прочитать до следующей пары символов слэш-символа. Это, вероятно, легче сделать с использованием символа с помощью ввода символов, чем по очереди; вы, по крайней мере, должны иметь возможность чередовать анализ символов с помощью линейного ввода.

Когда вы будете готовы к этому, вы можете попробовать этот тест на пытку в своей программе. Это то, что я использую для проверки моего стриппера комментария, SCC (который не обрабатывает триграфы - сознательным решением, если источник содержит триграфы, у меня есть средство для удаления триграфа, которое я использую сначала в источнике).

/* 
@(#)File:   $RCSfile: scc.test,v $ 
@(#)Version:   $Revision: 1.7 $ 
@(#)Last changed: $Date: 2013/09/09 14:06:33 $ 
@(#)Purpose:   Test file for program SCC 
@(#)Author:   J Leffler 
*/ 

/*TABSTOP=4*/ 

// -- C++ comment 

/* 
Multiline C-style comment 
#ifndef lint 
static const char sccs[] = "@(#)$Id: scc.test,v 1.7 2013/09/09 14:06:33 jleffler Exp $"; 
#endif 
*/ 

/* 
Multi-line C-style comment 
with embedded /* in line %C% which should generate a warning 
if scc is run with the -w option 
Two comment starts /* embedded /* in line %C% should generate one warning 
*/ 

/* Comment */ Non-comment /* Comment Again */ Non-Comment Again /* 
Comment again on the next line */ 

// A C++ comment with a C-style comment marker /* in the middle 
This is plain text under C++ (C99) commenting - but comment body otherwise 
// A C++ comment with a C-style comment end marker */ in the middle 

The following C-style comment end marker should generate a warning 
if scc is run with the -w option 
*/ 
Two of these */ generate */ one warning 

It is possible to have both warnings on a single line. 
Eg: 
*/ /* /* */ */ 

SCC has been trained to handle 'q' single quotes in most of 
the aberrant forms that can be used. '\0', '\\', '\'', '\\ 
n' (a valid variant on '\n'), because the backslash followed 
by newline is elided by the token scanning code in CPP before 
any other processing occurs. 

This is a legitimate equivalent to '\n' too: '\ 
\n', again because the backslash/newline processing occurs early. 

The non-portable 'ab', '/*', '*/', '//' forms are handled OK too. 

The following quote should generate a warning from SCC; a 
compiler would not accept it. ' 
\n' 

" */ /* SCC has been trained to know about strings /* */ */"! 
"\"Double quotes embedded in strings, \\\" too\'!" 
"And \ 
newlines in them" 

"And escaped double quotes at the end of a string\"" 

aa '\\ 
n' OK 
aa "\"" 
aa "\ 
\n" 

This is followed by C++/C99 comment number 1. 
// C++/C99 comment with \ 
continuation character \ 
on three source lines (this should not be seen with the -C flag) 
The C++/C99 comment number 1 has finished. 

This is followed by C++/C99 comment number 2. 
/\ 
/\ 
C++/C99 comment (this should not be seen with the -C flag) 
The C++/C99 comment number 2 has finished. 

This is followed by regular C comment number 1. 
/\ 
*\ 
Regular 
comment 
*\ 
/
The regular C comment number 1 has finished. 

/\ 
\/ This is not a C++/C99 comment! 

This is followed by C++/C99 comment number 3. 
/\ 
\ 
\ 
/But this is a C++/C99 comment! 
The C++/C99 comment number 3 has finished. 

/\ 
\* This is not a C or C++ comment! 

This is followed by regular C comment number 2. 
/\ 
*/ This is a regular C comment *\ 
but this is just a routine continuation *\ 
and that was not the end either - but this is *\ 
\ 
/
The regular C comment number 2 has finished. 

This is followed by regular C comment number 3. 
/\ 
\ 
\ 
\ 
* C comment */ 
The regular C comment number 3 has finished. 

Note that \u1234 and \U0010FFF0 are legitimate Unicode characters 
(officially universal character names) that could appear in an 
id\u0065ntifier, a '\u0065' character constant, or in a "char\u0061cter\ 
string". Since these are mapped long after comments are eliminated, 
they cannot affect the interpretation of /* comments */. In particular, 
none of \u0002A. \U0000002A, \u002F and \U0000002F ever constitute part 
of a comment delimiter ('*' or '/'). 

More double quoted string stuff: 

    if (logtable_out) 
    { 
    sprintf(logtable_out, 
     "insert into %s (bld_id, err_operation, err_expected, err_sql_stmt, err_sql_state)" 
     " values (\"%s\", \"%s\", \"%s\", \"", str_logtable, blade, operation, expected); 
    /* watch out for embedded double quotes. */ 
    } 


/* Non-terminated C-style comment at the end of the file 
0
#include <stdio.h> 

size_t counter(FILE *fp){ 
    int ch, chn; 
    size_t count = 0; 
    enum { none, in_line_comment, in_range_comment, in_string, in_char_constant } status; 
#if 0 
    in_range_comment : /* this */ 
    in_line_comment : //this 
    in_string : "this" 
    in_char_constnt : ' ' 
#endif 

    status = none; 
    while(EOF!=(ch=fgetc(fp))){ 
     switch(status){ 
     case in_line_comment : 
      if(ch == '\n'){ 
       status = none; 
      } 
      ++count; 
      continue; 
     case in_range_comment : 
      if(ch == '*'){ 
       chn = fgetc(fp); 
       if(chn == '/'){ 
        status = none; 
        continue; 
       } 
       ungetc(chn, fp); 
      } 
      ++count; 
      continue; 
     case in_string : 
      if(ch == '\\'){ 
       chn = fgetc(fp); 
       if(chn == '"'){ 
        continue; 
       } 
       ungetc(chn, fp); 
      } else { 
       if(ch == '"') 
        status = none; 
      } 
      continue; 
     case in_char_constant : 
      if(ch == '\\'){ 
       chn = fgetc(fp); 
       if(chn == '\''){ 
        continue; 
       } 
       ungetc(chn, fp); 
      } else { 
       if(ch == '\'') 
        status = none; 
      } 
      continue; 
     case none : 
      switch(ch){ 
      case '/': 
       if('/' == (chn = fgetc(fp))){ 
        status = in_line_comment; 
        continue; 
       } else if('*' == chn){ 
        status = in_range_comment; 
        continue; 
       } else 
        ungetc(chn, fp); 
       break; 
      case '"': 
       status = in_string; 
       break; 
      case '\'': 
       status = in_char_constant; 
       break; 
      } 
     } 
    } 
    return count; 
} 

int main(void){ 
    FILE *fp = stdin; 
    size_t c = counter(fp); 
    printf("%lu\n", c); 

    return 0; 
} 
Смежные вопросы