Code xóa comment trong file code

Anh có nhầm với trường hợp có space giữa * và / không ạ?
Do em thử comment cái này /* crazy */ trong C thôi thì nó vẫn được cho là comment mà nhỉ

Ủa, ý bạn là sao?

/* là mở đầu cho 1 comment block, không kể có space đằng sau hay không.

Dạ vâng, thì ý em là some/* crazy */stuff em cho ra KQ là somestuff thì đúng rồi mà anh :3
Nên ý em là em đang thắc mắc sao cái B vấn đề nằm ở đâu ấy ạ

Quy tắc của block comment là greedy, một khi đã mở /* thì chỉ cần gặp */ đầu tiên là coi như đó là đóng 1 block.

1 Like

Hic, em hoàn toàn đồng ý ạ.
Ý là em biết some/*crazy /*crazy*/*/stuff sẽ cho ra KQ đúng là some*/stuff. Nhưng KQ mà em chạy là ra some*crazy*/*/stuff nên em mới thắc mắc không biết mình đã lỗi hay quên xét điều kiện phần nào

Hiện tại bạn đang đặt tên biến hơi lằng nhằng.

Bạn nên sửa c1 thành current_char (kí tự bạn đang đọc trên file), kết hợp xét kí tự trước đó (previous_char). Khởi tạo previous_char = ' ' (kí tự trắng không làm thay đổi logic).

Mỗi lần lặp current_char:

  • nếu current_char == '"', bạn kiểm tra liệu có ngoặc kép " nào đang mở hay không, thông qua 1 biến is_quote_opening (nhận giá trị 0 hoặc 1). Nếu có thì gán is_quote_opening = 0, nếu không thì is_quote_opening = 1 (bắt đầu tạo 1 quote mới).

  • nếu current_char == '*'previous_char == '/', nếu không có ngoặc kép nào đang mở và cũng không có block comment nào đang mở thì đây sẽ là điểm bắt đầu của 1 block comment mới. Gán biến is_block_comment_opening bằng 1.

  • nếu current_char == '/'previous_char == '*', nếu có block comment nào đang mở thì đây sẽ là điểm kết thúc của block comment hiện tại. Gán biến is_block_comment_opening bằng 0.

  • kiểm tra vài điều kiện nữa (nếu cần)

  • gán previous_char = current_char ở cuối vòng lặp này.

Sơ lược là vậy, bạn tự sửa thêm nhé.


Test case mới:

/* "abc" */
"/* abc */"
/* " */
" /* "
" /* " */
/* " */ "
1 Like

Em sẽ tiếp tục check, nhưng em thấy hơi đuối với người mới học như em khi phải thực hiện cả phần " ".
Nhưng nếu chỉ xét case để xử lý trường hợp 3 thì anh có gợi ý nào dễ dàng cho em không?
Em đang suy nghĩ đến trường hợp tạo 1 biến int signal, khi đã nhận diện là signal thì không xét đến quotes nữa mà chỉ xét comment

Anh có thể cho em hỏi vì sao /* abc */ mà anh đưa ra lại không hoạt động không ạ?

Bài này mà bạn làm theo lexical analysis, lập 1 bảng quy tắc chung cho tất cả thì code không khó (code lúc này không phụ thuộc vào luật đóng/mở quote hay comment block).

Gợi ý xét current_charprevious_char của mình ở trên là phiên bản đơn giản nhất rồi. Dễ dàng hơn chỉ có cài đặt solution chuẩn (lexical analysis).

"/* abc */" là 1 literal string, string đó chứa gì không quan trọng.

1 Like

Này đúng kiểu vừa code vừa khóc luôn :slight_smile:
/* "abc" */ -> Output: Rỗng
"/* abc */" -> Output như cũ
/* " */ -> Output rỗng
" /* " -> Như cũ
" /* " */ -> Như cũ
/* " */ " -> Còn "
Không biết như vậy đã đúng với KQ anh muốn chưa ấy.

Nhưng nói đi nói lại thì cái test số 3 ngay từ đầu em vẫn 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;
    int signal = 0; 
    puts("Enter file name for checking comments: ");
    scanf("%s", &fileName);
    int line = 1;
    if((inFilePtr = fopen(fileName, "r")) != NULL)
    {
        
        FILE *outFilePtr;
        if((outFilePtr = fopen("result.txt", "w")) != NULL)
        {
            while((c1 = fgetc(inFilePtr)) != EOF)
            {
                if(c1 == '\n')
                {
                    line++;
                }
                if(c1 == '\"' && signal != 1)
                {
                    
                    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(c2 == '\n')
                {
                    line++;
                }
                
                if((c1 == '/') && (c2 == '*'))
                {
                    signal = 1;
                    c2 = fgetc(inFilePtr);
         
                    while((c1 != '*') && (c2 != '/'))
                    {      
                        c1 = c2;
                        c2 = fgetc(inFilePtr);
                        if(c2 == EOF)
                        {
                            puts("Error: Unterminated comment");
                            printf("Comment began at line %d", line);
                            break;
                        }
                    }
                    signal = 0;
                }
                
                else
                {
                    if(c1 != EOF)
                    {
                        fputc(c1, outFilePtr);
                    }
                    
                    if(c2 != EOF)
                    {
                        fputc(c2, outFilePtr);
                    }
                    
                }
            }
            fclose(outFilePtr);
        }
        else
        {
            puts("Problem occured when creating a file for new output");
        }
        
        fclose(inFilePtr);
        
    }
    else
    {
        printf("File \"%s\" couldn't be opened\n", fileName);
    }
}

Quy tắc có phải mình muốn là được đâu bạn :neutral_face: bạn bỏ thử lên IDE xem chữ nào không có màu xám thì biết ngay thôi.

1 Like

Em nghĩ em chỉ sai mỗi cái " /* " */, trên IDE thì cái */ cuối là màu xám, nhưng em không hiểu, vì phần phía trước trong IDE vẫn xanh (tức là em hiểu “” là xanh, nhưng cái cuối thì không hiểu)
Nhưng mà anh có biết nguyên nhân vì sao em sai trường hợp 3 có thể gợi ý giúp em được không ạ?

Mình không biết C nên khi thấy code bạn nhìn toàn fgets với các điều kiện lồng nhau, mình thấy rất rối.

Bạn cần code lại với 1 phiên bản khác sáng sủa hơn và thử theo logic mình gợi ý:

Bạn liên tục muốn gợi ý, nhưng khi mình đưa gợi ý thì bạn lại không thử 1 lần xem sao. Code theo thì có chết được đâu mà lo.

Em cũng cố gắng update code để xử lý cho các trường hợp mới mà anh.
Nhưng mà em tự nhận là xử lý hết đống này là hơi quá sức với em, nhưng để em sửa thêm.
Với lại như em trình bày về cách chạy của trường hợp 3, thì em chỉ muốn hiểu là một lý do vì sao mà kí tự * chỗ đó lại bị lọt thỏm ở lại.

#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 current_char, next_char;
    int signal = 0; 
    puts("Enter file name for checking comments: ");
    scanf("%s", &fileName);
    int line = 1;
    
    if((inFilePtr = fopen(fileName, "r")) != NULL)
    {
        
        FILE *outFilePtr;
        
        if((outFilePtr = fopen("result.txt", "w")) != NULL)
        {
            while((current_char = fgetc(inFilePtr)) != EOF)
            {
                if(current_char == '\n')
                {
                    line++;
                }
                if(current_char == '\"' && signal != 1)
                {
                    fputc(current_char, outFilePtr);
                    current_char = fgetc(inFilePtr);
                    
                    while(current_char != '\"')
                    {
                        fputc(current_char, outFilePtr);
                        current_char = fgetc(inFilePtr);
                        if(current_char == EOF)
                        {
                            
                            puts("Error");
                            break;
                        }
                        
                    }
                    fputc(current_char, outFilePtr);
                    current_char = fgetc(inFilePtr);
                   
                }
                
                next_char = fgetc(inFilePtr);
                
                if(next_char == '\n')
                {
                    line++;
                }
                
                if((current_char == '/') && (next_char == '*'))
                {
                    signal = 1;
                    next_char = fgetc(inFilePtr);
         
                    while((current_char != '*') && (next_char != '/'))
                    {      
                        current_char = next_char;
                        next_char = fgetc(inFilePtr);
                        if(next_char == EOF)
                        {
                            puts("Error: Unterminated comment");
                            printf("Comment began at line %d", line);
                            break;
                        }
                    }
                    
                    signal = 0;
                }
                else
                {
                    if(current_char != EOF)
                    {
                        fputc(current_char, outFilePtr);
                    }
                    
                    if(next_char != EOF)
                    {
                        fputc(next_char, outFilePtr);
                    }
                }
            }
            fclose(outFilePtr);
        }
        else
        {
            puts("Problem occured when creating a file for new output");
        }
        
        fclose(inFilePtr);
    }
    else
    {
        printf("File \"%s\" couldn't be opened\n", fileName);
    }
}

Code này có khác gì chỉ thay tên biến code cũ đâu bạn :pouting_cat:

Việc dự đoán trạng thái dựa vào kí tự hiện tại và kí tự trước đó dễ hơn rất nhiều so với việc tiên đoán trạng thái dựa vào kí tự tiếp theo. Riêng việc xử lý kí tự tiếp theo là EOF đã phức tạp rồi.

Ý anh là cùng một phương pháp như vậy, nhưng việc giả định kí tự phía trước là space và xét kí tự next_character ở đây là kí tự đầu tiên sẽ dễ hơn trong việc xử lý?

Việc xử lý 2 thông tin đã biết (kí tự hiện tại và kí tự trước đó) rõ ràng dễ đoán trạng thái hơn nhiều, và chỉ cần đọc kí tự 1 lần duy nhất trong 1 vòng xử lý.

Bạn xét theo hướng next_character là tự chui đầu vào bụi rậm.

Dạ để em thử lại rồi gửi anh, xong bài này em đầu thai luôn :frowning:

Bài có chút éc mà đã nói đến chuyện đầu thai là sao :pouting_cat:

Bạn cứ thử theo hướng của mình đi, bạn sợ thử thì chết hay sao vậy :pouting_cat:

GIAI ĐOẠN 1: XỬ LÝ QUOTES [Em đang cảm giác mình phải cố viết lại từ đầu với cùng 1 nội dung]

if((inFilePtr = fopen(fileName, "r")) != NULL)
    {
        
        FILE *outFilePtr;
        
        if((outFilePtr = fopen("result.txt", "w")) != NULL)
        {
            current_char = fgetc(inFilePtr);
            
            if(current_char == '"' && is_quote_opening == 0)
            {
                fputc(current_char, outFilePtr);
                while((current_char = fgetc(inFilePtr)) != '"')
                {
                    fputc(current_char, outFilePtr);
                    if(current_char == EOF)
                    {
                        puts("Error"); 
                    }
                }
            }
        }
    }
    else
    {
        //Báo lỗi
    }

Em làm hướng đó có đúng ý anh chỉ không để em ráng sửa tiếp, chứ 3 AM rồi mở mắt không ra.

Bạn đọc current_char 1 lần thôi, tránh chồng chéo các vòng lặp và cũng dễ mở rộng code sau này.

while (current_char = fgetc(inFilePtr)) {
    if(current_char == '"') {
        ...
    }
    else if (current_char == '*' && previous_char == '/') {
        ...
    }
    // các else if khác...
    else {
        // TRƯỜNG HỢP TẤT CẢ CÁC KÍ TỰ CÒN LẠI
        // chỉ xử lý current_char, đừng ham hố while gì nữa cả
    }
    previous_char = current_char;
}

Đi ngủ cho sáng trí, dậy rồi code tiếp :pouting_cat:

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