From e98093da948c35ab8d6d87039b4ebd355b63fdcc Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Thu, 12 Mar 2026 02:41:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0counter.py=E9=80=82=E9=85=8D?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- counter.py | 163 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) create mode 100644 counter.py diff --git a/counter.py b/counter.py new file mode 100644 index 00000000..3711cea5 --- /dev/null +++ b/counter.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +import os +import sys +import argparse +from pathlib import Path + +if sys.platform == "win32": + sys.stdout.reconfigure(encoding="utf-8") + sys.stderr.reconfigure(encoding="utf-8") + + +def count_lines(file_path: Path) -> int: + try: + with open(file_path, "r", encoding="utf-8", errors="ignore") as f: + return sum(1 for _ in f) + except Exception: + return 0 + + +def should_include(file_path: Path, extensions: list[str]) -> bool: + if not extensions: + return True + return file_path.suffix in extensions + + +def walk_dir(root_path: Path, extensions: list[str], exclude_dirs: set[str]): + results = [] + total_lines = 0 + + for root, dirs, files in os.walk(root_path): + dirs[:] = [d for d in dirs if d not in exclude_dirs] + + rel_root = Path(root).relative_to(root_path) + indent = len(rel_root.parts) if str(rel_root) != "." else 0 + prefix = " " * indent + + if indent > 0: + print(f"{prefix}└── {rel_root.name}/") + + for i, file in enumerate(files): + file_path = Path(root) / file + + if not should_include(file_path, extensions): + continue + + lines = count_lines(file_path) + total_lines += lines + results.append((file_path, lines, indent + 1)) + + is_last = (i == len(files) - 1) and not any( + should_include(Path(root) / f, extensions) for f in dirs + ) + connector = "└──" if is_last else "├──" + + print(f"{' ' * (indent + 1)}{connector} {file} ({lines} lines)") + + return total_lines + + +def main_all(): + directories = { + ".": [".cpp", ".h", ".hlsl"], + "docs": [".md"], + } + + total_all = 0 + results = [] + + for directory, extensions in directories.items(): + root_path = Path(directory) + if not root_path.exists(): + continue + + exclude_dirs = { + ".git", + "build", + "Release", + "bin", + "__pycache__", + ".ruff_cache", + "stbi", + } + + print(f"\n{'=' * 60}") + print(f"项目文件统计: {root_path}") + print(f"后缀过滤: {extensions}") + print(f"{'=' * 60}\n") + + total = walk_dir(root_path, extensions, exclude_dirs) + results.append((directory, total)) + total_all += total + + # 单独统计 stbi + stbi_path = Path("stbi") + if stbi_path.exists(): + exclude_dirs = {".git", "__pycache__"} + print(f"\n{'=' * 60}") + print(f"项目文件统计: stbi (第三方库)") + print(f"后缀过滤: ['.cpp', '.h']") + print(f"{'=' * 60}\n") + + total = walk_dir(stbi_path, [".cpp", ".h"], exclude_dirs) + results.append(("stbi", total)) + total_all += total + + print(f"\n{'=' * 60}") + print("汇总统计") + print(f"{'=' * 60}") + for name, lines in results: + print(f"{name:15} {lines:>10,} 行") + print(f"{'=' * 60}") + print(f"{'总计':15} {total_all:>10,} 行") + print(f"{'=' * 60}\n") + + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser(description="统计项目文件行数") + parser.add_argument( + "-e", "--extension", action="append", help="指定后缀名,如: .py .ts .js" + ) + parser.add_argument("-d", "--directory", default=".", help="指定子文件夹路径") + parser.add_argument( + "-x", "--exclude", action="append", default=[], help="排除的文件夹" + ) + parser.add_argument( + "--all", + action="store_true", + help="统计所有源码目录 (./docs/stbi)", + ) + + args = parser.parse_args() + + if args.all: + main_all() + else: + root_path = Path(args.directory).resolve() + extensions = args.extension + exclude_dirs = { + ".git", + "build", + "Release", + "bin", + "__pycache__", + } + exclude_dirs.update(args.exclude) + + if not root_path.exists(): + print(f"错误: 目录 {root_path} 不存在") + sys.exit(1) + + print(f"\n{'=' * 60}") + print(f"项目文件统计: {root_path}") + if extensions: + print(f"后缀过滤: {extensions}") + print(f"{'=' * 60}\n") + + total = walk_dir(root_path, extensions, exclude_dirs) + + print(f"\n{'=' * 60}") + print(f"总行数: {total}") + print(f"{'=' * 60}\n")