交个朋友,长个技能🥎🤸🏼‍♂️

新概念3

标准Markdown生成提示词

import os
import re
import argparse
from pathlib import Path
from typing import List, Dict, Tuple, Optional
import sys

class SmartMarkdownExtractor:
    def __init__(self, base_dir: str = "output", overwrite: bool = False):
        """
        初始化智能Markdown解析器
        
        Args:
            base_dir: 输出文件的基础目录
            overwrite: 是否覆盖已存在的文件
        """
        self.base_dir = Path(base_dir)
        self.overwrite = overwrite
        self.base_dir.mkdir(parents=True, exist_ok=True)
        
        # 常见文件扩展名
        self.file_extensions = {
            'python': ['.py', '.pyw', '.pyx'],
            'javascript': ['.js', '.jsx', '.mjs'],
            'typescript': ['.ts', '.tsx'],
            'html': ['.html', '.htm'],
            'css': ['.css'],
            'markdown': ['.md', '.markdown'],
            'json': ['.json'],
            'yaml': ['.yaml', '.yml'],
            'xml': ['.xml'],
            'sql': ['.sql'],
            'bash': ['.sh', '.bash'],
            'text': ['.txt', '.text'],
            'java': ['.java'],
            'cpp': ['.cpp', '.c', '.h', '.hpp', '.hxx'],
            'go': ['.go'],
            'rust': ['.rs'],
            'php': ['.php'],
            'ruby': ['.rb'],
            'swift': ['.swift'],
            'kotlin': ['.kt', '.kts'],
            'scala': ['.scala'],
            'dart': ['.dart'],
            'csharp': ['.cs'],
            'vb': ['.vb'],
            'perl': ['.pl', '.pm'],
            'lua': ['.lua'],
            'r': ['.r', '.R'],
            'matlab': ['.m'],
            'shell': ['.sh', '.zsh', '.bash', '.fish'],
            'batch': ['.bat', '.cmd'],
            'powershell': ['.ps1'],
            'dockerfile': ['.dockerfile', 'Dockerfile'],
            'makefile': ['Makefile', 'makefile'],
            'gradle': ['.gradle', '.gradle.kts'],
            'config': ['.cfg', '.conf', '.config', '.ini', '.toml'],
            'csv': ['.csv'],
            'tsv': ['.tsv'],
            'log': ['.log'],
            'lock': ['.lock'],
        }
        
        # 代码块语言到扩展名的映射
        self.lang_to_ext = {
            'python': '.py',
            'py': '.py',
            'javascript': '.js',
            'js': '.js',
            'typescript': '.ts',
            'ts': '.ts',
            'html': '.html',
            'css': '.css',
            'markdown': '.md',
            'md': '.md',
            'json': '.json',
            'yaml': '.yml',
            'yml': '.yml',
            'xml': '.xml',
            'sql': '.sql',
            'bash': '.sh',
            'shell': '.sh',
            'text': '.txt',
            'java': '.java',
            'cpp': '.cpp',
            'c': '.c',
            'c++': '.cpp',
            'rust': '.rs',
            'go': '.go',
            'ruby': '.rb',
            'php': '.php',
            'swift': '.swift',
            'kotlin': '.kt',
            'scala': '.scala',
            'dart': '.dart',
            'csharp': '.cs',
            'cs': '.cs',
            'vb': '.vb',
            'perl': '.pl',
            'lua': '.lua',
            'r': '.r',
            'matlab': '.m',
            'powershell': '.ps1',
            'ps1': '.ps1',
            'batch': '.bat',
            'dockerfile': 'Dockerfile',
            'makefile': 'Makefile',
        }
    
    def clean_filename(self, filename: str) -> str:
        """
        清理文件名,移除特殊字符和多余的空白
        """
        # 移除反引号
        filename = filename.replace('`', '')
        
        # 移除开头和结尾的空白
        filename = filename.strip()
        
        # 移除可能存在的语言标记
        if ' ' in filename:
            parts = filename.split(' ', 1)
            if parts[0].lower() in self.lang_to_ext:
                filename = parts[1]
        
        return filename
    
    def extract_path_from_title(self, title: str) -> Tuple[Optional[str], str, Optional[str]]:
        """
        从标题中提取目录、文件名和扩展名
        
        Returns:
            (目录, 文件名, 扩展名)
        """
        # 清理标题
        title = self.clean_filename(title)
        
        # 移除标题标记(# 和空格)
        title = title.lstrip('#').strip()
        
        # 检查是否包含路径分隔符
        if '/' in title:
            # 分割目录和文件名
            parts = title.rsplit('/', 1)
            if len(parts) == 2:
                directory = parts[0].strip()
                filename = parts[1].strip()
            else:
                directory = None
                filename = title.strip()
        else:
            directory = None
            filename = title.strip()
        
        # 提取扩展名
        extension = None
        if '.' in filename:
            # 找到最后一个点作为扩展名分隔符
            parts = filename.rsplit('.', 1)
            if len(parts) == 2:
                extension = '.' + parts[1]
        
        return directory, filename, extension
    
    def parse_markdown_titles(self, markdown_text: str) -> List[Dict]:
        """
        解析Markdown中的所有标题,提取文件信息
        
        Returns:
            包含文件信息的字典列表
        """
        files = []
        lines = markdown_text.split('\n')
        
        i = 0
        while i < len(lines):
            line = lines[i]
            
            # 检查是否是标题行(以1-6个#开头)
            if line.startswith('#') and line.lstrip('#').startswith(' '):
                # 提取标题内容
                title = line.strip()
                
                # 从标题中提取路径信息
                directory, filename, extension = self.extract_path_from_title(title)
                
                # 查找标题对应的内容
                content_start = i + 1
                content_end = self.find_content_end(lines, content_start)
                
                # 提取内容
                content_lines = lines[content_start:content_end]
                content = '\n'.join(content_lines)
                
                # 清理内容中的代码块标记
                content = self.extract_code_block_content(content)
                
                # 如果内容不为空,添加到文件列表
                if content.strip():
                    # 如果没有文件名(比如标题只是描述性的),使用默认文件名
                    if not filename:
                        filename = self.generate_filename(len(files), content)
                    
                    # 如果没有扩展名,尝试从内容或语言推断
                    if not extension:
                        extension = self.guess_extension(content, filename)
                        if extension:
                            filename = filename + extension
                    
                    files.append({
                        'title': title,
                        'directory': directory,
                        'filename': filename,
                        'extension': extension,
                        'content': content.strip(),
                        'title_line': i + 1,  # 1-based line number
                    })
                
                i = content_end
            else:
                i += 1
        
        return files
    
    def find_content_end(self, lines: List[str], start_idx: int) -> int:
        """
        找到标题内容的结束位置
        
        Returns:
            内容结束的行索引
        """
        # 跳过开头的空行
        i = start_idx
        while i < len(lines) and not lines[i].strip():
            i += 1
        
        # 如果没有内容,直接返回
        if i >= len(lines):
            return len(lines)
        
        # 检查下一行是否是标题
        if lines[i].startswith('#') and lines[i].lstrip('#').startswith(' '):
            return i
        
        # 找到下一个标题的位置
        for j in range(i, len(lines)):
            if lines[j].startswith('#') and lines[j].lstrip('#').startswith(' '):
                # 找到下一个标题,内容结束
                return j
        
        # 如果没有找到更多标题,内容直到文件结束
        return len(lines)
    
    def extract_code_block_content(self, content: str) -> str:
        """
        从内容中提取代码块内容
        
        如果内容中包含代码块,提取代码块内的内容
        否则返回原始内容
        """
        lines = content.split('\n')
        result = []
        in_codeblock = False
        codeblock_lang = None
        
        for line in lines:
            stripped = line.strip()
            
            # 检查是否是代码块开始
            if stripped.startswith('```'):
                if in_codeblock:
                    # 代码块结束
                    in_codeblock = False
                    codeblock_lang = None
                else:
                    # 代码块开始
                    in_codeblock = True
                    # 提取语言
                    lang_part = stripped[3:].strip()
                    if lang_part:
                        codeblock_lang = lang_part.split()[0] if lang_part else None
            elif in_codeblock:
                # 在代码块内,添加内容
                result.append(line)
            elif not in_codeblock and stripped:
                # 不在代码块内,但也不是空行,也作为内容
                result.append(line)
        
        return '\n'.join(result)
    
    def generate_filename(self, index: int, content: str) -> str:
        """
        生成默认文件名
        """
        # 尝试从内容中提取有意义的名称
        lines = content.split('\n')
        for line in lines[:10]:  # 只检查前10行
            line = line.strip()
            # 查找常见的命名模式
            patterns = [
                r'def\s+(\w+)',  # Python函数定义
                r'class\s+(\w+)',  # Python类定义
                r'function\s+(\w+)',  # JavaScript函数
                r'const\s+(\w+)\s*=',  # JavaScript常量
                r'let\s+(\w+)\s*=',  # JavaScript变量
                r'var\s+(\w+)\s*=',  # JavaScript变量
                r'interface\s+(\w+)',  # TypeScript接口
                r'type\s+(\w+)',  # TypeScript类型
            ]
            
            for pattern in patterns:
                match = re.search(pattern, line)
                if match:
                    name = match.group(1).lower()
                    if len(name) > 2 and len(name) < 20:
                        return name
        
        # 如果没有找到合适的名称,使用默认名称
        return f"file_{index + 1}"
    
    def guess_extension(self, content: str, filename: str) -> Optional[str]:
        """
        根据内容和文件名猜测文件扩展名
        """
        lines = content.split('\n')
        first_line = lines[0].strip() if lines else ""
        
        # 检查内容中的线索
        shebang_patterns = [
            (r'^#!/usr/bin/env python', '.py'),
            (r'^#!/usr/bin/python', '.py'),
            (r'^#!/bin/bash', '.sh'),
            (r'^#!/bin/sh', '.sh'),
            (r'^#!/usr/bin/env node', '.js'),
            (r'^#!/usr/bin/env ruby', '.rb'),
            (r'^#!/usr/bin/env perl', '.pl'),
        ]
        
        for pattern, ext in shebang_patterns:
            if re.match(pattern, first_line):
                return ext
        
        # 检查常见的关键词
        content_lower = content.lower()
        if 'def ' in content_lower or 'import ' in content_lower or 'from ' in content_lower:
            if 'print(' in content_lower or 'class ' in content_lower:
                return '.py'
        
        if 'function ' in content_lower or 'const ' in content_lower or 'let ' in content_lower:
            if 'console.' in content_lower or 'document.' in content_lower:
                return '.js'
        
        if '<!DOCTYPE html>' in content_lower or '<html' in content_lower:
            return '.html'
        
        if 'SELECT ' in content_lower or 'INSERT INTO' in content_lower or 'CREATE TABLE' in content_lower:
            return '.sql'
        
        if '---' in first_line and ('key:' in content_lower or 'value:' in content_lower):
            return '.yml'
        
        if '{' in content_lower and '}' in content_lower and ':' in content_lower:
            # 可能是JSON或JavaScript对象
            try:
                import json
                json.loads(content)
                return '.json'
            except:
                pass
        
        # 根据文件名猜测
        for lang, exts in self.file_extensions.items():
            for ext in exts:
                if filename.endswith(ext):
                    return ext
        
        # 默认使用.txt
        return '.txt'
    
    def save_extracted_files(self, files: List[Dict]) -> Dict:
        """
        保存提取的文件
        
        Returns:
            处理结果的统计信息
        """
        stats = {
            'total': len(files),
            'success': 0,
            'failed': 0,
            'skipped': 0,
            'files': []
        }
        
        for file_info in files:
            filename = file_info['filename']
            directory = file_info['directory']
            content = file_info['content']
            
            # 构建完整路径
            if directory:
                full_path = self.base_dir / directory / filename
                # 创建目录
                (self.base_dir / directory).mkdir(parents=True, exist_ok=True)
            else:
                full_path = self.base_dir / filename
            
            # 检查文件是否已存在
            if full_path.exists() and not self.overwrite:
                print(f"⚠  跳过: {full_path} (文件已存在)")
                stats['skipped'] += 1
                continue
            
            try:
                # 保存文件
                with open(full_path, 'w', encoding='utf-8') as f:
                    f.write(content)
                
                # 记录成功
                relative_path = str(full_path.relative_to(self.base_dir))
                stats['files'].append({
                    'path': relative_path,
                    'size': len(content),
                    'line': file_info['title_line']
                })
                stats['success'] += 1
                
            except Exception as e:
                print(f"✗  失败: {filename} - {e}")
                stats['failed'] += 1
        
        return stats
    
    def process(self, markdown_text: str, source_name: str = None) -> Dict:
        """
        处理Markdown文本
        
        Returns:
            处理结果的统计信息
        """
        print("=" * 60)
        print("智能Markdown解析器")
        print("=" * 60)
        
        if source_name:
            print(f"📄 源文件: {source_name}")
        
        print(f"📂 输出目录: {self.base_dir.absolute()}")
        print("-" * 60)
        
        # 解析Markdown标题
        print("🔍 正在解析标题...")
        files = self.parse_markdown_titles(markdown_text)
        
        if not files:
            print("❌ 未找到任何标题")
            return {'total': 0, 'success': 0, 'failed': 0, 'skipped': 0, 'files': []}
        
        print(f"✅ 找到 {len(files)} 个标题")
        print("-" * 60)
        
        # 显示找到的文件
        print("📋 找到的文件:")
        for i, file_info in enumerate(files, 1):
            if file_info['directory']:
                path = f"{file_info['directory']}/{file_info['filename']}"
            else:
                path = file_info['filename']
            
            print(f"  {i:2d}. {path}")
            print(f"      标题: {file_info['title'][:50]}...")
            print(f"      行号: 第{file_info['title_line']}行")
            print(f"      大小: {len(file_info['content'])} 字符")
        
        print("-" * 60)
        print("💾 正在保存文件...")
        
        # 保存文件
        stats = self.save_extracted_files(files)
        
        # 显示统计信息
        print("-" * 60)
        print("📊 统计信息:")
        print(f"  总计: {stats['total']} 个文件")
        print(f"  成功: {stats['success']} 个")
        print(f"  失败: {stats['failed']} 个")
        if stats['skipped'] > 0:
            print(f"  跳过: {stats['skipped']} 个 (文件已存在)")
        
        if stats['success'] > 0:
            print("\n✅ 已保存的文件:")
            for file_info in stats['files']:
                print(f"  📄 {file_info['path']} ({file_info['size']} 字符)")
        
        print("=" * 60)
        return stats


def main():
    parser = argparse.ArgumentParser(
        description='从Markdown标题中智能提取文件并保存到相应目录',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
示例:
  %(prog)s input.md                     # 解析文件
  %(prog)s input.md --output ./project  # 指定输出目录
  %(prog)s input.md --overwrite         # 覆盖已存在的文件
  %(prog)s --stdin                      # 从标准输入读取

支持的标题格式:
  # src/main.py                         → src/main.py
  # utils/helper.py                     → utils/helper.py
  # README.md                           → README.md
  # config/settings.yaml                → config/settings.yaml
  # 这是一个说明文件                    → 这是一个说明文件.txt
  # 函数定义                            → 函数定义.py (自动推断)
        '''
    )
    
    parser.add_argument(
        'input_file',
        nargs='?',
        help='输入的Markdown文件'
    )
    
    parser.add_argument(
        '-o', '--output',
        default='output',
        help='输出目录 (默认: output)'
    )
    
    parser.add_argument(
        '--overwrite',
        action='store_true',
        help='覆盖已存在的文件'
    )
    
    parser.add_argument(
        '--stdin',
        action='store_true',
        help='从标准输入读取'
    )
    
    parser.add_argument(
        '-q', '--quiet',
        action='store_true',
        help='安静模式,减少输出'
    )
    
    args = parser.parse_args()
    
    # 获取Markdown内容
    markdown_text = ""
    source_name = None
    
    if args.stdin:
        # 从标准输入读取
        if not args.quiet:
            print("从标准输入读取Markdown内容...")
        markdown_text = sys.stdin.read()
        source_name = "stdin"
    elif args.input_file:
        # 从文件读取
        try:
            with open(args.input_file, 'r', encoding='utf-8') as f:
                markdown_text = f.read()
            source_name = args.input_file
        except FileNotFoundError:
            print(f"错误: 文件 '{args.input_file}' 不存在")
            return 1
        except Exception as e:
            print(f"读取文件时出错: {e}")
            return 1
    else:
        parser.print_help()
        return 0
    
    # 创建解析器
    extractor = SmartMarkdownExtractor(
        base_dir=args.output,
        overwrite=args.overwrite
    )
    
    # 处理Markdown
    stats = extractor.process(markdown_text, source_name)
    
    return 0 if stats['failed'] == 0 else 1


if __name__ == "__main__":
    exit(main())

发表评论