
需求分析Python一键统计你的本地文件夹或压缩包的代码行数可处理任意层嵌套压缩包、文件夹、多个或单个代码文件其中统计部分包含代码行数、空白行数、注释行数、总行数。这对于自己学习或开发的小白来说可以清楚地知道自己过去一周、一个月在本地机器写了多少行代码。回到顶部使用教程此时输入合适的绝对路径或文件夹含单个文件作为检测目标。嵌套压缩包听说是日本开发需求输入py -3 count_code_Ultra.py “your_path运行结果图如下单个文件py -3 count_code_Ultra.py “C:\Users\your_mid_path\count_code_Ultra.py(要使用绝对路径。文件夹py -3 count_code_Ultra.py C:\Users\your_last_path\注意内部不使用引号也是可以的而且csv、xlsx文件的代码行数不计数统计当中文件数数量计入统计当中。回到顶部代码示例# -*- coding: utf-8 -*-import osimport sysimport argparseimport urllib.parseimport tempfileimport shutilimport zipfileimport tarfileEXTENSIONS {.py: python,.js: javascript,.ts: typescript,.jsx: javascript,.tsx: typescript,.html: html,.css: css,.sql: sql,.sh: shell,.bat: batch,.ps1: powershell,.md: markdown,.json: json,.yaml: yaml,.yml: yaml,.xml: xml,.txt: text,.conf: config,.ini: config,.env: env,.db: database,.c: c,.h: c,.cpp: cpp,.cc: cpp,.cxx: cpp,.hpp: cpp,.java: java,.go: go,.rs: rust,.swift: swift,.kt: kotlin,.scala: scala,.rb: ruby,.php: php,.pl: perl,.lua: lua,.r: r,.m: objective-c,.mm: objective-c,}ARCHIVE_EXTENSIONS {.zip, .tar, .tar.gz, .tar.bz2, .tar.zst, .tgz, .gz, .bz2, .7z, .rar}SKIP_DIRS {__pycache__, .git, .venv, venv, node_modules, .pytest_cache, .mypy_cache, dist, build, .egg-info}def url_to_path(url_or_path):path url_or_path.strip()if path.startswith(file://):path path.replace(file:///, ).replace(file://, )elif :// in path:parsed urllib.parse.urlparse(path)path parsed.path if parsed.path else parsed.netlocpath path.replace(/, os.sep).replace(\\, os.sep)if path.startswith(\\) and : not in path:path path[1:]return path.rstrip(\\)def split_archive_path(path):if os.path.exists(path):return path, lower_path path.lower()all_extensions [.tar.gz, .tar.bz2, .tar.zst, .tgz, .zip, .7z, .rar, .tar, .gz, .bz2]all_candidates []for ext in all_extensions:ext_lower ext.lower()start 0while True:idx lower_path.find(ext_lower, start)if idx -1:breakarchive_path path[:idx len(ext)]sub_path path[idx len(ext):].lstrip(\\).lstrip(/)all_candidates.append((idx, archive_path, sub_path))start idx 1all_candidates.sort(keylambda x: x[0])for idx, archive_path, sub_path in all_candidates:if os.path.exists(archive_path):return archive_path, sub_pathreturn path, def get_archive_ext(filename):name filename.lower()if name.endswith(.tar.gz):return .tar.gzelif name.endswith(.tar.bz2):return .tar.bz2elif name.endswith(.tar.zst):return .tar.zstfor ext in [.tar.gz, .tar.bz2, .tar.zst, .tgz, .zip, .tar, .gz, .bz2, .7z, .rar]:if name.endswith(ext):return extreturn os.path.splitext(filename)[1].lower()def extract_archive(archive_path, extract_dir):ext get_archive_ext(archive_path)base_name os.path.splitext(archive_path)[0]if ext in [.tar.gz, .tar.bz2, .tar.zst]:base_name os.path.splitext(base_name)[0]temp_dir tempfile.mkdtemp()try:if ext .zip:with zipfile.ZipFile(archive_path, r) as zip_ref:zip_ref.extractall(temp_dir)return temp_direlif ext .tar:with tarfile.open(archive_path, r) as tar:tar.extractall(temp_dir, filterdata)return temp_direlif ext .tar.gz or ext .tgz:with tarfile.open(archive_path, r:gz) as tar:tar.extractall(temp_dir, filterdata)return temp_direlif ext .tar.bz2:with tarfile.open(archive_path, r:bz2) as tar:tar.extractall(temp_dir, filterdata)return temp_direlif ext .tar.zst:try:import zstandardwith tarfile.open(archive_path, r:bz2) as tar:tar.extractall(temp_dir, filterdata)return temp_direxcept ImportError:passelif ext .gz:import gzipoutput_file os.path.join(temp_dir, os.path.basename(base_name))with gzip.open(archive_path, rb) as f_in:with open(output_file, wb) as f_out:shutil.copyfileobj(f_in, f_out)return temp_direlif ext .bz2:import bz2output_file os.path.join(temp_dir, os.path.basename(base_name))with bz2.open(archive_path, rb) as f_in:with open(output_file, wb) as f_out:shutil.copyfileobj(f_in, f_out)return temp_direlif ext .7z:import py7zrwith py7zr.SevenZipFile(archive_path, r) as sz:sz.extractall(temp_dir)return temp_direlif ext .rar:try:import rarfilewith rarfile.RarFile(archive_path, r) as rf:rf.extractall(temp_dir)return temp_direxcept ImportError:try:import subprocessunrar_path rC:\Program Files\WinRAR\unrar.exeresult subprocess.run([unrar_path, x, -o, archive_path, temp_dir], capture_outputTrue)if result.returncode 0:return temp_direxcept Exception:passexcept Exception as e:print(Warning: Failed to extract archive_path : str(e))if temp_dir and os.path.exists(temp_dir):try:shutil.rmtree(temp_dir)except Exception:passreturn Nonedef extract_zip_with_subdir(zip_path, sub_path, extract_dir):temp_dir tempfile.mkdtemp()try:with zipfile.ZipFile(zip_path, r) as zip_ref:if sub_path:for member in zip_ref.namelist():if member.startswith(sub_path /) or member sub_path:zip_ref.extract(member, temp_dir)new_base temp_dirif sub_path:actual_sub os.path.join(temp_dir, sub_path)if os.path.exists(actual_sub):new_base actual_subreturn new_baseelse:zip_ref.extractall(temp_dir)return temp_direxcept Exception as e:print(Warning: Failed to extract zip_path subdir sub_path : str(e))if temp_dir and os.path.exists(temp_dir):try:shutil.rmtree(temp_dir)except Exception:passreturn Nonedef count_lines(file_path):try:with open(file_path, r, encodingutf-8, errorsignore) as f:lines f.readlines()except Exception:try:with open(file_path, r, encodinggbk, errorsignore) as f:lines f.readlines()except Exception:return 0, 0, 0, 0total_lines len(lines)code_lines 0blank_lines 0comment_lines 0in_multiline_comment Falseext os.path.splitext(file_path)[1].lower()for line in lines:stripped line.strip()if ext .py:if stripped.startswith() or stripped.startswith():comment_lines 1if stripped.count() 2 or stripped.count() 2:continuein_multiline_comment not in_multiline_commentcontinueif in_multiline_comment:comment_lines 1continueif stripped.startswith(#):comment_lines 1continueelif ext in [.js, .ts, .jsx, .tsx, .html, .css, .java, .c, .cpp, .h, .m, .mm, .swift, .kt, .scala, .go, .rs, .rb, .php, .pl, .lua, .sh]:if /* in stripped and */ in stripped:comment_lines 1continueif /* in stripped:comment_lines 1in_multiline_comment Truecontinueif */ in stripped:comment_lines 1in_multiline_comment Falsecontinueif in_multiline_comment:comment_lines 1continueif stripped.startswith(//):comment_lines 1continueelif ext .bat or ext .ps1:if stripped.startswith(REM) or stripped.startswith(::):comment_lines 1continueif not stripped:blank_lines 1else:code_lines 1return total_lines, code_lines, blank_lines, comment_linesdef scan_directory(target_path, is_rootTrue):total_files 0total_lines 0code_lines 0blank_lines 0comment_lines 0file_stats {}nested_archives []for dirpath, dirnames, filenames in os.walk(target_path):dirnames[:] [d for d in dirnames if d not in SKIP_DIRS]for filename in filenames:file_path os.path.join(dirpath, filename)ext get_archive_ext(filename)if ext in ARCHIVE_EXTENSIONS:nested_archives.append(file_path)continueif ext not in EXTENSIONS:if ext in (.xlsx, .xls, .xlsb, .xlsm, .csv):total_files 1lang excelif lang not in file_stats:file_stats[lang] {files: 0, lines: 0, code: 0, comment: 0}file_stats[lang][files] 1continuetotal_files 1total, code, blank, comment count_lines(file_path)total_lines totalcode_lines codeblank_lines blankcomment_lines comment