Code xóa comment trong file code

Chào các bác,
Chuyện là em có thử một bài tập về việc loại bỏ comment dạng /* … */ trong C.
Các phần khai báo cơ bản thì em nghĩ cũng dễ hiểu, chủ yếu em thắc mắc trong đoạn này:

if((outFilePtr = fopen("result.c", "w")) != NULL) //Đọc file cần check để xóa comment
{
    while((c1 = fgetc(inFilePtr)) != EOF) //Check kí tự đầu tiên
    {
        c2 = fgetc(inFilePtr); //Lưu giá trị của kí tự tiếp theo

        if((c1 == '/') && (c2 == '*')) //Nếu bắt đầu là comment với dạng /*
        {
            c2 = fgetc(inFilePtr);
 
            while((c1 != '*') && (c2 != '/')) //Lần đầu chạy sẽ luôn đúng vì c1 đang ở vị trí /
            {      
                c1 = c2;
                c2 = fgetc(inFilePtr);
            }
        }
        
        else // Lưu vào file mới bởi vì if đã sai
        {
            fputc(c1, outFilePtr);
        }
    }
}

Cho em hỏi như nhìn chung thì cách xử lý của em có vấn đề gì không ạ?
Chẳng hạn như ở vòng while phía trong, em xét đến trường hợp xấu nhất là chỉ có /* nhưng lại không có đóng thì while có tự động kết thúc khi đến EOF không, hay nó sẽ kiểu chạy vô tận và lỗi?
Ngoài ra, em muốn thông báo ra screen là trường hợp đó sẽ bị lỗi nếu không có đóng comment thì em đang hướng tới giải pháp sử dụng thêm if dạng như sau:

while((c1 != '*') && (c2 != '/')) //Lần đầu chạy sẽ luôn đúng vì c1 đang ở vị trí /
{      
    c1 = c2;
    c2 = fgetc(inFilePtr);
    if(c2 == EOF) //Tức là đến cuối file mà vẫn chưa có đóng comment 
    {
            puts("Error");
    }
}

Không biết có anh chị nào có thể gợi ý cho em 1 tí để mình hoàn thiện hơn được không :3

2 posts were merged into an existing topic: Topic lưu trữ các post off-topic - version 3

Nhấn vào phần sửa bài để xem bài được sửa như thế nào để mà học theo.

Mình đọc sơ qua thì thấy thế này (nếu không nhầm):

/* ... */

Sẽ cho kết quả đúng.


Nhưng thế này:

 /* ... */

Sẽ cho kết quả sai.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define BUF_SIZE 64
int main(int argc, char **argv)
{
	FILE *inFilePtr;
    char fileName[BUF_SIZE];
    char c1, c2;
    puts("Enter file name for checking comments: ");
    scanf("%s", &fileName);
    
    if((inFilePtr = fopen(fileName, "r")) != NULL)
    {
        
        FILE *outFilePtr;
        if((outFilePtr = fopen("result.txt", "w")) != NULL)
        {
            while((c1 = fgetc(inFilePtr)) != EOF)
            {
                c2 = fgetc(inFilePtr);
     
                if((c1 == '/') && (c2 == '*'))
                {
                    c2 = fgetc(inFilePtr);
         
                    while((c1 != '*') && (c2 != '/'))
                    {      
                        c1 = c2;
                        c2 = fgetc(inFilePtr);
                    }
                }
                
                else
                {
                    fputc(c1, outFilePtr);
                    if(c2 != EOF)
                    {
                        fputc(c2, outFilePtr);
                    }
                    
                }
            }
        }
        else
        {
            puts("");
        }
        
        fclose(inFilePtr);
        
    }
    else
    {
        printf("File \"%s\" couldn't be opened\n", fileName);
    }
}

Dạ đây là code full mà em đã hoàn thiện

Em sẽ cho chạy qua 6 cách test

  1. some/* crazy */stuff
  • Output: somestuff -> ĐÚNG
  1. some/* crazy */ stuff
  • Output: some stuff -> ĐÚNG (Em không hiểu sao hướng dẫn lại bảo em chú ý cách test thử của 1 và 2)
  1. some/*crazy /*crazy*/*/stuff
  • Output: some*crazy*/*/stuff SAI (Em chưa hiểu sao ra KQ như này)
  • Em nghĩ kết quả đúng sẽ là some*/stuff
  1. ”some /* crazy */ stuff ”
  • Output: ”some stuff ” (Em nghĩ là đúng, vì em đang cân nhắc cho trường hợp comment /* */)
  • Tuy nhiên, em vẫn đang chưa biết làm sao để có KQ là “some stuff” thay vì như trên
  1. some/* ”crazy” */ stuff
  • Output: some stuff (ĐÚNG)
  1. some /* crazy stuff
  • Chưa thử, đây là câu mà em muốn hỏi anh chị gợi ý làm sao để output ra màn hình là SAI.

Bạn lưu ý ở (4)
Chuỗi trong C bắt đầu và kết thúc bằng dấu nháy đôi. Nên:

"some /* crazy */ stuff"

kết quả vẫn là "some /* crazy */ stuff".

Còn nhiều nữa…

1 Like

Gợi ý là bài này phải dùng stack nhé.

Note nhẹ là bài này hơi liên quan đến automata rồi. Bạn tìm hiểu lexical analysis nhé.

1 vài test case bạn có thể thử:

/*abc*/
/**/
/******/
/*a*/
//
/*/
4 Likes

Dạ em cảm ơn, như mục 6 thì em đã tìm ra giải pháp, đó là thêm điều kiện vào vòng lặp như sau:

while((c1 != '*') && (c2 != '/'))
                    {      
                        c1 = c2;
                        c2 = fgetc(inFilePtr);
                        if(c2 == EOF)
                        {
                            puts("Error");
                            break;
                        }
                    }

Đã test thử và chạy thành công.
Tuy nhiên câu 3 vẫn là cái em thắc mắc nhất, vì nếu em nhẩm thì khi đạt vị trí /* sau chữ some, thì c1 và c2 đã tăng lên rồi, vậy tại sao nó lại lọt ra được kí tự * ngay giữa some và crazy được ta :3
Còn ý anh nhiều cái nữa tức là em còn sai nhiều hay sao ạ?

Anh cho em hỏi cái automata này là thuộc kiểu học phần của môn nào vậy ạ?
Để có gì em tìm hiểu thêm.
Do em mới bắt đầu học cái gọi là “Computers System and Low Level Programming” nhưng chưa thấy khái niệm này bao giờ.
Nhưng nếu nói rộng hơn thì những case như " … ", và trong double quotes có chứa /* */ thì em phải viết một phần riêng để xử lý cái này nữa phải không anh? Vì em cảm thấy nếu xét trong double quote thì giống như kiểu em duplicate cho code xử lý comment /* */

Bạn sẽ được học về Automata trong môn Toán rời rạc.

Lúc này bạn phải viết 1 bộ lexical analysis riêng, lập thêm quy tắc cho cặp " " nữa.

2 Likes

Em cảm ơn nhiều ạ, em sẽ tìm hiểu thêm case này và update sớm.
Nếu bỏ qua trường hợp trên và em chỉ xét đến việc quan tâm /* */ thì em nghĩ em chỉ sai ở trường hợp số 3.
Không biết trường hợp này anh có gợi ý gì cho em không ạ?

Code phải chạy ra kết quả, không có “nghĩ là sai” đâu bạn.

Ý anh là em phải hoàn thiện cả case " " và xử lý trường hợp 3 thì mới là đúng KQ ạ?

Đúng vậy, bạn cần lọc đúng comment chứ không thể chỉ lọc đại theo cặp /* */ được.

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define BUF_SIZE 64 
int main(int argc, char **argv)
{
	FILE *inFilePtr;
    char fileName[BUF_SIZE];
    char c1, c2;
    puts("Enter file name for checking comments: ");
    scanf("%s", &fileName);
    
    if((inFilePtr = fopen(fileName, "r")) != NULL)
    {
        
        FILE *outFilePtr;
        if((outFilePtr = fopen("result.txt", "w")) != NULL)
        {
            while((c1 = fgetc(inFilePtr)) != EOF)
            {
                
                if(c1 == '\"')
                {
                    fputc(c1, outFilePtr);
                    c1 = fgetc(inFilePtr);
                    while(c1 != '\"')
                    {
                        fputc(c1, outFilePtr);
                        c1 = fgetc(inFilePtr);
                        if(c1 == EOF)
                        {
                            
                            puts("Error");
                            break;
                        }
                        
                    }
                    fputc(c1, outFilePtr);
                    c1 = fgetc(inFilePtr);
                   
                }
                c2 = fgetc(inFilePtr);
                if((c1 == '/') && (c2 == '*'))
                {
                    c2 = fgetc(inFilePtr);
         
                    while((c1 != '*') && (c2 != '/'))
                    {      
                        c1 = c2;
                        c2 = fgetc(inFilePtr);
                        if(c2 == EOF)
                        {
                            puts("Error");
                            break;
                        }
                    }
                }
                
                else
                {
                    if(c1 != EOF)
                    {
                        fputc(c1, outFilePtr);
                    }
                    
                    if(c2 != EOF)
                    {
                        fputc(c2, outFilePtr);
                    }
                    
                }
            }
        }
        else
        {
            puts("");
        }
        
        fclose(inFilePtr);
        
    }
    else
    {
        printf("File \"%s\" couldn't be opened\n", fileName);
    }
}

Em gửi anh ạ, em nghĩ em đã xử lý được cả trường hợp của quotes, nhưng nói thật em nhìn code mình viết vẫn thấy nó dở sao sao ấy :frowning:
Như trường hợp 3 anh có thể gợi ý cho em 1 tí không

Đáp án nằm toàn bộ ở hình ảnh automata kia rồi nhé.

Cơ mà em vẫn kiểu bị khó hiểu 1 tí ấy.
Ví dụ
A. some/* crazy */stuff
B. some/*crazy /*crazy*/*/stuff
Ít ra nó đều giống nhau ở phần đầu là some/* nhưng cái B lại vẫn còn output vào file mới dấu * giữa some và crazy ấy anh

A sẽ cho ra /* crazy */ còn B sẽ cho ra /*crazy /*crazy*/ nhé.

Em test thử thì A cho ra somestuff là KQ em mong muốn mà nhỉ?
Chỉ có mỗi cái B là ra trật thôi

À ý mình là phần comment sẽ ra như thế này

83% thành viên diễn đàn không hỏi bài tập, còn bạn thì sao?