MISRA-C diary(C言語日誌)

2014年12月の記事一覧

C言語の注釈を削除する。

C言語の注釈を削除して、C言語の機能の単語帳を作る。

C言語の注釈を削除するには、いろいろな道具がある。

1 C言語で書く
05/30  C言語ソースファイルのコメントを削除する

上記のソースに下記のように注釈を追加して、処理してみた。
// Remove C comment from C programming Languge Source Code File
/* 2014.12.29 Dr. OGAWA Kiyoshi Add comment for test this program work well.*/
#include<stdio.h> 
#include<stdlib.h> 
/*
 Main function use argument, input file name
 Out put should be standard out.
*/
int main(int argc,char*argv[]){
    FILE* fp;
    char* buf;
    int reteral=0;
    size_t i,len; // i:counter, len:string length

    if (argc>1&&(fp=fopen(argv[1],"r"))){
        fseek(fp,0,SEEK_END);
        len=ftell(fp);
        buf=(char*)malloc(len);rewind(fp);
        fread(buf,len,1,fp);
        fclose(fp); 

        for(i=0;i<len;i++) {
            if(!reteral&&buf[i]=='/'&&(i+1)<len&&buf[i+1]=='/') {
                i+=2; 
                while(i<len&&buf[i]!='\n')i++;
            } else if(!reteral&&buf[i]=='/'&&(i+1)<len&&buf[i+1]=='*') {
                i+=2; 
                while(i<len&&!(buf[i]=='*'&&(i+1)<len&&buf[i+1]=='/'))i++;
                i+=2;
            } else if((buf[i]=='\"'||buf[i]=='\'')&&(buf[i-1]!='\\')) {
                reteral=!reteral;
            }
            putchar(buf[i]);
        }
    }
//printf("// This file is remove /* */ and //");
    return 0;
}
処理結果は下記。
#include<stdio.h> 
#include<stdlib.h> 

int main(int argc,char*argv[]){
    FILE* fp;
    char* buf;
    int reteral=0;
    size_t i,len; 

    if (argc>1&&(fp=fopen(argv[1],"r"))){
        fseek(fp,0,SEEK_END);
        len=ftell(fp);
        buf=(char*)malloc(len);rewind(fp);
        fread(buf,len,1,fp);
        fclose(fp); 

        for(i=0;i<len;i++) {
            if(!reteral&&buf[i]=='/'&&(i+1)<len&&buf[i+1]=='/') {
                i+=2; 
                while(i<len&&buf[i]!='\n')i++;
            } else if(!reteral&&buf[i]=='/'&&(i+1)<len&&buf[i+1]=='*') {
                i+=2; 
                while(i<len&&!(buf[i]=='*'&&(i+1)<len&&buf[i+1]=='/'))i++;
                i+=2;
            } else if((buf[i]=='\"'||buf[i]=='\'')&&(buf[i-1]!='\\')) {
                reteral=!reteral;
            }
            putchar(buf[i]);
        }
    }
//printf("
    return 0;
}
残念、論理のどこがまずいのだろう。人が作った論理は分かり難い。

2. C言語でCのソースコードからコメント文を取り除くプログラムの作り方を教えてください。
直接LLVMでコンパイルするとstrnicmpがないと言われる。そこで、下記のように代替関数に切替可能にして、注釈を付加し、処理してみた。
#include <stdio.h>
#include <string.h>

#define __cplusplus__strings__  // http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_71/rtref/stricmp.htm%23stricmp?lang=ja

void chomp(char *s)
{
  int len = strlen(s);
  if (len > 0 && s[len - 1] == '\n') s[len - 1] = '\0';
}

int parse(int f, const char *in, char *out)
{
  while(*in != '\0') {
    if (f) {      /* Cコメントの内部 */
      if (*in == '*') {
        in++;
        if (*in == '/') {
          f = 0;
          in++;
        }
      } else {
        in++;
      }
    } else {      /* Cコメントの外部 */
      if (*in == '/') {
        in++;
        if (*in == '*') {          /* Cコメントの内部 */
          in++;
          f = 1;
          continue;
        } else if (*in == '/') {          /* C++コメントの内部 */
          f = 0;
          break;
        } else {
          *out++ = '/';
        }
      }
      *out++ = *in++;
    }
  }
  *out = '\0';
  return f;
}

int main(int argc, char **argv)
{
  FILE *fin, *fout;
  char inbuf[1024], outbuf[1024];
  int f;

  if (argc != 3) {
    printf("使い方: %s \"入力ファイル\" \"出力ファイル\"\n", argv[0]);
    return 0;
  }
#ifndef __cplusplus__strings__ //http://d.hatena.ne.jp/Kuna/20090414/1239718852
  if (stricmp(argv[1], argv[2]) == 0) {
#else
  if (strcasecmp(argv[1], argv[2]) == 0) {
#endif
    printf("*/入力ファイルと出力ファイルは別々にしてください。\n");
    return 1;
  }
  fin = fopen(argv[1], "r");
  if (fin == NULL) {
    printf("%s: /* ファイルが開けません。\n", argv[1]);
    return 2;
  }
  fout = fopen(argv[2], "w");
  if (fout == NULL) {
    printf("%s: //ファイルが開けません。\n", argv[2]);
    fclose(fin);
    return 3;
  }
  f = 0;
  while(fgets(inbuf, 1024, fin) != NULL) {
    chomp(inbuf);
    f = parse(f, inbuf, outbuf);
    fprintf(fout, "%s\n", outbuf);
  }
  fclose(fin);
//fprintf(fout,"// This file is remove /* */ and //");
  fclose(fout);
  return 0;
}
//rmcc.c:58:7: warning: implicit declaration of function 'stricmp' is invalid in C99 [-Wimplicit-function-declaration]
//  if (stricmp(argv[1], argv[2]) == 0) {
//      ^
//1 warning generated.
/* Undefined symbols for architecture x86_64:
  "_stricmp", referenced from:
      _main in rmcc-f7c3ec.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation) */

文字列に/*があるとうまく処理できない。何か余分なことをしているかも。処理結果は下記。
#include <stdio.h>
#include <string.h>

#define __cplusplus__strings__  

void chomp(char *s)
{
  int len = strlen(s);
  if (len > 0 && s[len - 1] == '\n') s[len - 1] = '\0';
}

int parse(int f, const char *in, char *out)
{
  while(*in != '\0') {
    if (f) {      
      if (*in == '*') {
        in++;
        if (*in == '/') {
          f = 0;
          in++;
        }
      } else {
        in++;
      }
    } else {      
      if (*in == '/') {
        in++;
        if (*in == '*') {          
          in++;
          f = 1;
          continue;
        } else if (*in == '/') {          
          f = 0;
          break;
        } else {
          *out++ = '/';
        }
      }
      *out++ = *in++;
    }
  }
  *out = '\0';
  return f;
}

int main(int argc, char **argv)
{
  FILE *fin, *fout;
  char inbuf[1024], outbuf[1024];
  int f;

  if (argc != 3) {
    printf("使い方: %s \"入力ファイル\" \"出力ファイル\"\n", argv[0]);
    return 0;
  }
#ifndef __cplusplus__strings__ 
  if (stricmp(argv[1], argv[2]) == 0) {
#else
  if (strcasecmp(argv[1], argv[2]) == 0) {
#endif
    printf("*/入力ファイルと出力ファイルは別々にしてください。\n");
    return 1;
  }
  fin = fopen(argv[1], "r");
  if (fin == NULL) {
    printf("%s: 















 and 
  fclose(fout);
  return 0;
}

不要な行末空白やコメント文字列を一括削除する正規表現

/\*.*\*/
上記と同様、文字列中に/*があると、次の*/まで削除する。
間が注釈であるかどうかは関係なく。

4. ・C言語のコメントを消す
正規表現で消そうとしている。上記よりは複雑な対応を検討している。
文字列中の/*への対応は未記述。

5. 複数行コメントにマッチする正規表現
複数行コメントに対応するが、文字列中の/*への対応は未記述。

6. sedスクリプト
#! /bin/sed -nf

# Remove C and C++ comments, by Brian Hiles (brian_hiles@rocketmail.com)

# Sped up (and bugfixed to some extent) by Paolo Bonzini (bonzini@gnu.org)
# Works its way through the line, copying to hold space the text up to the
# first special character (/, ", ').  The original version went exactly a
# character at a time, hence the greater speed of this one.  But the concept
# and especially the trick of building the line in hold space are entirely
# merit of Brian.

:loop

# This line is sufficient to remove C++ comments!
/^\/\// s,.*,,

/^$/{
  x
  p
  n
  b loop
}
/^"/{
  :double
  /^$/{
    x
    p
    n
    /^"/b break
    b double
  }

  H
  x
  s,\n\(.[^\"]*\).*,\1,
  x
  s,.[^\"]*,,
  
  /^"/b break
  /^\\/{
    H
    x
    s,\n\(.\).*,\1,
    x
    s/.//
  }
  b double
}

/^'/{
  :single
  /^$/{
    x
    p
    n
    /^'/b break
    b single
  }
  H
  x
  s,\n\(.[^\']*\).*,\1,
  x
  s,.[^\']*,,
  
  /^'/b break
  /^\\/{
    H
    x
    s,\n\(.\).*,\1,
    x
    s/.//
  }
  b single
}

/^\/\*/{
  s/.//
  :ccom
  s,^.[^*]*,,
  /^$/ n
  /^\*\//{
    s/..//
    b loop
  }
  b ccom
}

:break
H
x
s,\n\(.[^"'/]*\).*,\1,
x
s/.[^"'/]*//
b loop

7  script
def comment_remover(text):
def replacer(match):
s = match.group(0)
if s.startswith('/'):
return ""
else:
return s
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE
)
return re.sub(pattern, replacer, text)

8 gcc  -fpreprocessed -dD -E foo.c
clangは -fpreprocessedではない。
そこで、
gcc-4.9 -fpreprocessed -dD -E rmc.c

現在扱っている例では、コンパイルエラーになるものが入っている。
プリプロセスで警告が出る。

misrac2012_20150509.c:9089:0: warning: "BASE" redefined
 #define BASE "base"
 ^
misrac2012_20150509.c:6056:0: note: this is the location of the previous definition
 #define BASE 65024u
 ^
misrac2012_20150509.c:9494:2: error: invalid preprocessing directive #elsel
 #elsel        // ??K??
  ^
misrac2012_20150509.c:9502:2: error: invalid preprocessing directive #start
 #start is not a tokon in a comment 
0