您好,欢迎访问本站!登录后台
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧
  • 网站所有资源均来自网络,如有侵权请联系站长删除!

Python删除代码中的空行和#注释行

网络技术 中国人民很行 2025-11-16 110 次浏览 18个评论

处理多个空行

  • 多个连续空行合并为一个空行
  • 保留必要的代码分隔空行
  • 不会过度压缩空行

删除的注释

  • 整行注释:# 这是一个注释
  • 行内注释:vlan_info_cache = {} # 格式: {device_ip: {...}}

保留的内容

  • 文件头特殊注释
  • 字符串中的#号
  • 单个空行(用于代码分隔)


import os
import re

def remove_all_comments_keep_single_empty_line():
    """
    删除所有注释,多个空行合并为一个空行
    """
    current_dir = os.getcwd()
    print(f"当前目录: {current_dir}")
    
    # 查找当前目录下的所有py文件
    py_files = [f for f in os.listdir(current_dir) 
               if f.endswith('.py') and f != os.path.basename(__file__)]
    
    if not py_files:
        print("当前目录下没有找到Python文件!")
        return
    
    print(f"\n找到 {len(py_files)} 个Python文件:")
    for i, file in enumerate(py_files, 1):
        print(f"  {i}. {file}")
    
    # 确认操作
    confirm = input(f"\n是否继续清理这些文件的所有注释?(y/n): ").strip().lower()
    if confirm != 'y':
        print("操作已取消")
        return
    
    backup = input("是否创建备份文件?(y/n): ").strip().lower() == 'y'
    
    print("\n开始处理文件...")
    
    for file in py_files:
        print(f"\n处理文件: {file}")
        try:
            success = process_file_remove_comments_single_empty_line(file, backup)
            if success:
                print(f"✓ 完成: {file}")
            else:
                print(f"✗ 失败: {file}")
        except Exception as e:
            print(f"✗ 错误: {file} - {e}")

def process_file_remove_comments_single_empty_line(file_path, backup):
    """
    处理单个文件,删除注释,多个空行合并为一个空行
    """
    # 读取文件
    with open(file_path, 'r', encoding='utf-8') as file:
        lines = file.readlines()
    
    # 创建备份
    if backup:
        backup_path = file_path + '.bak'
        with open(backup_path, 'w', encoding='utf-8') as backup_file:
            backup_file.writelines(lines)
        print(f"  已创建备份: {backup_path}")
    
    # 需要保留的特殊注释模式(文件头部)
    preserve_patterns = [
        r'^# -\*- coding:.*-\*-',  # 编码声明
        r'^#.*coding:.*utf-8',     # 编码声明变体
        r'^#!.*python',            # shebang
        r'^#.*env python',         # shebang变体
    ]
    
    def should_preserve(line):
        """检查是否应该保留这行(文件头特殊注释)"""
        line_stripped = line.strip()
        if line_stripped.startswith('#'):
            for pattern in preserve_patterns:
                if re.match(pattern, line_stripped, re.IGNORECASE):
                    return True
        return False
    
    def remove_inline_comment(line):
        """
        移除行内注释,但保留字符串中的#号
        """
        # 如果是空行,直接返回
        if not line.strip():
            return line
        
        # 如果是应该保留的文件头注释,直接返回
        if should_preserve(line):
            return line
        
        in_single_quote = False
        in_double_quote = False
        in_triple_single = False
        in_triple_double = False
        escaped = False
        
        result = []
        i = 0
        n = len(line)
        
        while i < n:
            char = line[i]
            
            # 处理转义字符
            if char == '\\' and not escaped:
                escaped = True
                result.append(char)
                i += 1
                continue
            
            # 处理字符串状态
            if not escaped:
                # 三重单引号
                if not in_double_quote and not in_triple_double and i + 2 < n and line[i:i+3] == "'''":
                    in_triple_single = not in_triple_single
                    result.append("'''")
                    i += 2
                # 三重双引号
                elif not in_single_quote and not in_triple_single and i + 2 < n and line[i:i+3] == '"""':
                    in_triple_double = not in_triple_double
                    result.append('"""')
                    i += 2
                # 单引号
                elif not in_double_quote and not in_triple_single and not in_triple_double and char == "'":
                    in_single_quote = not in_single_quote
                    result.append(char)
                # 双引号
                elif not in_single_quote and not in_triple_single and not in_triple_double and char == '"':
                    in_double_quote = not in_double_quote
                    result.append(char)
                # 注释开始(不在字符串中)
                elif char == '#' and not in_single_quote and not in_double_quote and not in_triple_single and not in_triple_double:
                    # 找到注释开始,删除行剩余部分
                    break
                else:
                    result.append(char)
            else:
                result.append(char)
                escaped = False
            
            i += 1
        
        cleaned_content = ''.join(result)
        # 保留原始行的换行符
        if line.endswith('\n'):
            return cleaned_content.rstrip() + '\n'
        else:
            return cleaned_content.rstrip()
    
    # 第一步:移除注释
    intermediate_lines = []
    removed_comments = []
    
    for line in lines:
        if should_preserve(line):
            # 保留文件头特殊注释
            intermediate_lines.append(line)
        elif line.strip() and line.strip().startswith('#'):
            # 整行注释,替换为空行但保留换行符
            removed_comments.append(line.strip())
            if line.endswith('\n'):
                intermediate_lines.append('\n')
            else:
                intermediate_lines.append('')
        else:
            # 处理其他行(可能包含行内注释)
            cleaned_line = remove_inline_comment(line)
            intermediate_lines.append(cleaned_line)
    
    # 第二步:合并多个连续空行为一个空行
    cleaned_lines = []
    previous_was_empty = False
    
    for line in intermediate_lines:
        is_empty = not line.strip()  # 检查是否是空行
        
        if is_empty:
            if not previous_was_empty:
                # 第一个空行,保留
                cleaned_lines.append(line)
                previous_was_empty = True
            else:
                # 连续的空行,跳过
                continue
        else:
            # 非空行
            cleaned_lines.append(line)
            previous_was_empty = False
    
    # 写回文件
    with open(file_path, 'w', encoding='utf-8') as file:
        file.writelines(cleaned_lines)
    
    # 统计
    original_line_count = len(lines)
    cleaned_line_count = len(cleaned_lines)
    removed_comment_count = len(removed_comments)
    
    # 计算合并的空行数
    original_empty_count = len([line for line in lines if not line.strip()])
    cleaned_empty_count = len([line for line in cleaned_lines if not line.strip()])
    merged_empty_count = original_empty_count - cleaned_empty_count
    
    print(f"  原始行数: {original_line_count}, 清理后行数: {cleaned_line_count}")
    print(f"  移除了 {removed_comment_count} 行注释")
    if merged_empty_count > 0:
        print(f"  合并了 {merged_empty_count} 个空行")
    
    # 显示被删除的注释示例
    if removed_comments:
        print("  删除的注释示例:")
        for i, comment in enumerate(removed_comments[:3]):
            print(f"    - {comment}")
        if len(removed_comments) > 3:
            print(f"    ... 还有 {len(removed_comments) - 3} 行")
    
    return True

def preview_comments_removal_single_empty_line():
    """
    预览注释删除,多个空行合并为一个
    """
    current_dir = os.getcwd()
    py_files = [f for f in os.listdir(current_dir) 
               if f.endswith('.py') and f != os.path.basename(__file__)]
    
    if not py_files:
        print("当前目录下没有找到Python文件!")
        return
    
    print(f"\n找到 {len(py_files)} 个Python文件:")
    for i, file in enumerate(py_files, 1):
        print(f"  {i}. {file}")
    
    file_choice = input("\n请输入要预览的文件编号: ").strip()
    try:
        file_index = int(file_choice) - 1
        if 0 <= file_index < len(py_files):
            file_path = py_files[file_index]
            preview_file_comments_single_empty_line(file_path)
        else:
            print("无效的文件编号!")
    except ValueError:
        print("请输入有效的数字!")

def preview_file_comments_single_empty_line(file_path):
    """
    预览单个文件的注释删除,多个空行合并为一个
    """
    with open(file_path, 'r', encoding='utf-8') as file:
        original_lines = file.readlines()
    
    # 需要保留的特殊注释模式
    preserve_patterns = [
        r'^# -\*- coding:.*-\*-',
        r'^#.*coding:.*utf-8',
        r'^#!.*python',
        r'^#.*env python',
    ]
    
    def should_preserve(line):
        line_stripped = line.strip()
        if line_stripped.startswith('#'):
            for pattern in preserve_patterns:
                if re.match(pattern, line_stripped, re.IGNORECASE):
                    return True
        return False
    
    def remove_inline_comment_simple(line):
        """简化版的行内注释移除"""
        if not line.strip() or should_preserve(line):
            return line
        
        in_string = False
        string_char = None
        result = []
        i = 0
        n = len(line)
        
        while i < n:
            char = line[i]
            
            if char in ('"', "'") and not in_string:
                in_string = True
                string_char = char
                result.append(char)
            elif in_string and char == string_char:
                in_string = False
                string_char = None
                result.append(char)
            elif char == '#' and not in_string:
                break
            else:
                result.append(char)
            
            i += 1
        
        cleaned_content = ''.join(result)
        if line.endswith('\n'):
            return cleaned_content.rstrip() + '\n'
        else:
            return cleaned_content.rstrip()
    
    # 第一步:移除注释
    intermediate_lines = []
    for line in original_lines:
        if should_preserve(line):
            intermediate_lines.append(line)
        elif line.strip() and line.strip().startswith('#'):
            # 整行注释,替换为空行
            if line.endswith('\n'):
                intermediate_lines.append('\n')
            else:
                intermediate_lines.append('')
        else:
            cleaned_line = remove_inline_comment_simple(line)
            intermediate_lines.append(cleaned_line)
    
    # 第二步:合并多个连续空行为一个空行
    cleaned_lines = []
    previous_was_empty = False
    
    for line in intermediate_lines:
        is_empty = not line.strip()
        
        if is_empty:
            if not previous_was_empty:
                cleaned_lines.append(line)
                previous_was_empty = True
        else:
            cleaned_lines.append(line)
            previous_was_empty = False
    
    print(f"\n{'='*60}")
    print(f"文件: {file_path}")
    print(f"{'='*60}")
    
    # 显示清理前后的对比
    print(f"\n{'='*30} 清理前 {'='*30}")
    display_count = 0
    line_number = 0
    empty_line_count = 0
    for line in original_lines:
        if display_count < 25:
            if not line.strip():
                empty_line_count += 1
                if empty_line_count <= 3:  # 只标记前3个空行
                    print(f"{line_number+1:3d}: [空行]")
                else:
                    print(f"{line_number+1:3d}: {line.rstrip()}")
            elif line.strip() and '#' in line and not should_preserve(line):
                if line.strip().startswith('#'):
                    print(f"\033[91m{line_number+1:3d}: {line.rstrip()}\033[0m")
                else:
                    highlighted = re.sub(r'(#.*)$', r'\033[91m\1\033[0m', line.rstrip())
                    print(f"{line_number+1:3d}: {highlighted}")
            else:
                print(f"{line_number+1:3d}: {line.rstrip()}")
            display_count += 1
        line_number += 1
    
    if len(original_lines) > 25:
        print("... (仅显示前25行)")
    
    print(f"\n{'='*30} 清理后 {'='*30}")
    display_count = 0
    line_number = 0
    for line in cleaned_lines:
        if display_count < 25:
            if not line.strip():
                print(f"{line_number+1:3d}: [空行]")
            else:
                print(f"{line_number+1:3d}: {line.rstrip()}")
            display_count += 1
        line_number += 1
    
    if len(cleaned_lines) > 25:
        print("... (仅显示前25行)")
    
    # 统计
    original_comments = len([line for line in original_lines 
                           if line.strip() and '#' in line and not should_preserve(line)])
    cleaned_comments = len([line for line in cleaned_lines 
                          if line.strip() and '#' in line and not should_preserve(line)])
    
    original_empty = len([line for line in original_lines if not line.strip()])
    cleaned_empty = len([line for line in cleaned_lines if not line.strip()])
    
    print(f"\n统计信息:")
    print(f"  移除注释数量: {original_comments - cleaned_comments}")
    print(f"  空行数量: {original_empty} -> {cleaned_empty}")
    print(f"  合并空行数量: {original_empty - cleaned_empty}")
    print(f"  总行数变化: {len(original_lines)} -> {len(cleaned_lines)}")
    
    confirm = input("\n是否应用更改?(y/n): ").strip().lower()
    if confirm == 'y':
        with open(file_path, 'w', encoding='utf-8') as file:
            file.writelines(cleaned_lines)
        print(f"已清理文件: {file_path}")

if __name__ == "__main__":
    print("Python注释清理工具 (合并空行版)")
    print("=" * 50)
    print("功能:")
    print("  - 删除所有整行注释和行内注释")
    print("  - 多个连续空行合并为一个空行")
    print("  - 保留文件头特殊注释")
    print("  - 保留字符串中的#号")
    print("\n示例:")
    print("  ✓ 3个连续空行 → 1个空行")
    print("  ✓ 保留必要的代码分隔空行")
    
    while True:
        print("\n请选择操作:")
        print("1. 清理当前目录所有Python文件的所有注释")
        print("2. 预览文件更改(合并空行)")
        print("3. 退出")
        
        choice = input("\n请输入选择 (1-3): ").strip()
        
        if choice == '1':
            remove_all_comments_keep_single_empty_line()
        elif choice == '2':
            preview_comments_removal_single_empty_line()
        elif choice == '3':
            print("再见!")
            break
        else:
            print("无效选择,请重新输入")


已有 110 位网友参与,快来吐槽:

发表评论

验证码