✅ 处理多个空行:
- 多个连续空行合并为一个空行
- 保留必要的代码分隔空行
- 不会过度压缩空行
✅ 删除的注释:
- 整行注释:# 这是一个注释
- 行内注释: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 位网友参与,快来吐槽:
发表评论