自动替换 Latex 中引用代码的行号

Latex 中引用代码的时候,可以不用包括整个文件,只用给起始和终止的行号,便可以嵌入需要的代码。但是,由于代码经常修改,导致起止行号变化,还需要手动更新该行号,否则编译生成的 pdf 文件中涉及到的代码会发生错位。

为此,编写了个简单的函数处理该行号,本程序适合 bash shell,其他程序如法炮制:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''
# 这个代码是问文心一言,然后给出的
# 问:我想用 python 写一段获取 Linux 中 shell 脚本文件的函数列表以及这些函数在文件中的起始和结束位置
# 回答:
#     在Python中,你可以使用os和re模块来读取并分析shell脚本。下面是一个简单的函数,
# 它可以获取shell脚本中的函数列表及其在文件中的起始位置。
#     这个函数假设你的shell脚本是Bash脚本,并且函数以function_name()的形式定义。
# 它使用正则表达式来匹配函数的开始和结束位置。

def get_shell_functions(file_path):  
    functions = []  
    with open(file_path, 'r') as file:  
        content = file.read()  
        pattern = r'\bfunction\s+(\w+)\s*\([^;{]*\)\s*{\s*(/\*.*?\*/|[^}]+)*\s*}'  
        matches = re.findall(pattern, content, re.MULTILINE | re.DOTALL)  
        for match in matches:  
            start = content.index(match)  
            end = start + len(match)  
            functions.append((match[0], start, end))  
    return functions
    
# 文心一言给的正则表达式不符合规则所以匹配出来乱七八糟,结果令人失望,只好自己编写!
'''

import os  
import re  

import BnPlatform.BinnyBase as BinnyBase
  
def get_shell_functions(content, print_code=False):
    '''
    得到某个 bash shell 的全部函数名称以及函数在代码中的起始和结束的位置
    '''
    functions = []  
    
    # 这个正则表达式主要作用是找到函数的开始位置,结尾位置由于函数体很复杂
    # 无法进行匹配,所以思路是先找到位置,再分析大括号封闭的情况
    # 当然也可能有转义大符号等语句,这个留给专业的词法分析器解析,这里只是简单的匹配就行
    # 匹配合法的函数表达式,function fn(){...}、fn(){...}
    pattern = r"(\s*[function]*?\s*)(\w+)\s*\(\s*\)\s*{.*?}"
    pos = 0
    while True:
        match = re.search(pattern, content[pos:], re.DOTALL)
        if not match:
            break
        start = match.start()
        end = match.end()
        # Move forward in text for the next search
        # 提取函数和位置 func_name
        func_name = match.groups()[1]
        # 匹配到的空格
        blanks = match.groups()[0]
        start_pos = pos + start + len(blanks)
        end_pos = pos + end
        # 找到真正的结尾,只有左右符号相等才是封闭的
        _, s, e = BinnyBase.bnGetBetweenSymbol(content[start_pos:], '{|}')
        # 如果分析错误,中间也许包含其他函数,或者只包含函数的部分
        # 这一点不勉强,至少这对我复杂的函数,也可以返回正确的结果
        if start_pos + e + 1 > end_pos:
            end_pos = start_pos + e + 1
        if print_code:
            print('{}[{:>2d} : {:>2d}]'.format(
                content[start_pos:end_pos], start_pos, end_pos - 1))
        functions.append((func_name, start_pos, end_pos))
        pos = end_pos
        
    return functions  


def convert_code_line_number(content, start, end):
    '''得到开始和结束位置在代码中的行号'''
    return [BinnyBase.bnCountsOf(content[:start], '\n') + 1,
           BinnyBase.bnCountsOf(content[:end], '\n') + 1]

def replace_latex_lstinputlisting(content,
                                  base_shell_name,
                                  row_begin=0, 
                                  row_end=0):
    '''替换 Latex 中 lstinputlisting 函数的起始和终止行号'''
    # 修改和脚本中的位置信息,示例脚本:
    # \lstinputlisting[language=bash,
    #                  caption=LoadBaseFunc.sh/openwrt\_compile\_source,
    #                  label=LoadBaseFunc.sh/openwrt_compile_source,
    #                  firstline=306, lastline=535, columns=flexible,
    #                  breaklines=true]{Script/sanple.sh}
    pattern = r'\\lstinputlisting\[language=bash.*?' + base_shell_name + '.*?}'
    # 由于只在一行内替换,所以不用正则表达式多行的标志    
    row_search = re.search(pattern, content)
    if row_search:
        row_code = content[row_search.start():row_search.end()]
        # 将上例中的 firstline=306, lastline=535 自动修改为最新的
        row_replace = re.sub(r"firstline=\s*?\d+", f"firstline={row_begin}", row_code)
        row_replace = re.sub(r"lastline=\s*?\d+", f"lastline={row_end}", row_replace)
        if row_replace != row_code:
            return content.replace(row_code, row_replace)
    return None
    


def main(shell_path, tex_files, print_code=False):
    # 需要修改的 Latex 代码
    content_shell = BinnyBase.bnGetFileData(shell_path)
    functions = get_shell_functions(content_shell, print_code=print_code)  
    if functions:
        for tex_file, func_name in tex_files:
            if not os.path.exists(tex_file):
                continue
            for function, start, end in functions:
                if function != func_name:
                    continue
                if print_code:
                    print(f"Function: {function}, Start: {start}, End: {end}")
                # 将位置转成行数
                row_begin, row_end = convert_code_line_number(content_shell, start, end)
                # 首先找到改行需要修改的代码
                base_shell_name = os.path.basename(shell_path)
                # 获取文件内容
                content = BinnyBase.bnGetFileData(tex_file)
                # 保存文件
                _content = replace_latex_lstinputlisting(
                    content, base_shell_name=base_shell_name,
                    row_begin=row_begin, row_end=row_end)
                if _content and _content != content:
                    if print_code:
                        print(f"正在替换: {tex_file} 文件中的函数 {function} 的起止位置 ...")
                    BinnyBase.bnSetFileData(tex_file, _content)                
                break


if __name__ == '__main__':
    print_code = False
    # Latex 代码文件和需要替换的函数体名称
    tex_files = []
    tex_files.append((r'../src/XXX.tex', 'xxx_func_name'))
    tex_files.append((r'../src/YYY.tex', 'yyy_func_name'))
    # 使用某个脚本,该脚本可能多次修改,这样可以自动找到修改以后的代码位置
    main(shell_path=r'./sanple.sh', tex_files=tex_files, print_code=print_code)

使用很简单,源码面前不用解释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值