代码中文抽取工具并替换工具(以ts为例)

news2025/6/9 9:25:22

文章目录

  • 基本思路
  • 目录结构
  • 配置文件
  • AST解析
  • 替换代码中文
  • 生成Excel
  • 启动脚本

基本思路

  1. 通过对应语言的AST解析出中文
  2. 相关信息(文件、所在行列等)存到临时文件
  3. 通过相关信息,逐个文件位置替换掉中文
  4. 基于临时文件,通过py脚本生成Excel表格

目录结构

在这里插入图片描述

配置文件

// config.json
{
    "_NOTE": "// _NOTE后缀都是注释, 无需修改",


    "start_id_NOTE": "// 生成配置表的起始id",
    "start_id": 10000,


    "funCls_NOTE": "// 静态替换函数的类名",
    "funCls": "LangUtil",


    "replaceFunc_NOTE": "// 替换函数, 形如: public static getLang(code: number, ...args): string; ",
    "replaceFunc": "LangUtil.getLang",


    "importAbsolute_NOTE": "// import的绝对路径",
    "importAbsolute": "import { LangUtil } from 'src/game/mod/common/utils/LangUtil';",


    "ignore_file_NOTE": "// 忽略的.ts文件名",
    "ignore_file": [
        "Const.ts",
        "CharConst.ts",
		"CharConstLog.ts",
		"CharConstFeat.ts",
		"CharConstMars.ts",
		"CharConstCommon.ts",
		"CharConstMeridian.ts",
		"LangUtil.ts"
    ],


    "ignore_dir_NOTE": "// 忽略的文件目录名",
    "ignore_dir": [
    ],


    "isReplaceStaticProperty_NOTE": "// 是否替换静态的属性的中文字符(建议第一波先不替换;第二波再统一替换,会报错,看能不能优化代码不要用静态的)",
    "isReplaceStaticProperty": false,

    "replacer_exe_NOTE": "// 是否执行代码中文替换",
    "replacer_exe": true,
    
    "gen_xls_exe_NOTE": "// 是否生成xls",
    "gen_xls_exe": true,

    "xls_name_NOTE": "// 生成的xls文件名",
    "xls_name": "lang_tool.xls",

    
    "OPTIONAL_PARAMS_NOTE": "// ==========================================下方一般都没必要修改=============================",
    "columns_NOTE": "// 生成表的列占位符",
    "columns": ["NOTE", "ID", "文本", "空列", "代码文件来源", "被替换的源代码"],
    
    "ecmaVersion_NOTE": "// 解析所需参数",
    "ecmaVersion": 2020,

    "sourceType_NOTE": "// 解析所需参数",
    "sourceType": "module",

    "genTsEnum_NOTE": "// 是否生成ts枚举类",
    "genTsEnum": true,

    "isPrettyJsn_NOTE": "// 是否美化的json",
    "isPrettyJsn": true,

    "tempJ_NOTE": " // json文件名",
    "tempJ": "tempJ.json"
}

AST解析

// tool_chinese_strings_extracted.js
const fs = require('fs');
const path = require('path');
const { parse } = require('@typescript-eslint/typescript-estree');
const readline = require('readline');

const POSTFIX_TS = ".ts"
let ENUM_START_ID = 1;
let tblStartId = 1; //表的id
const FILENAME_POSTFIX = "Lang";
const __Mark = "________";
const scriptDir = __dirname;
const CharConstClsName = "CharConst";
const IO_ENCODING = "utf8";
const COMBINE_NAME = `${CharConstClsName}${FILENAME_POSTFIX}`;
const CharConstTSFile = `${COMBINE_NAME}${POSTFIX_TS}`;
const input_dir = `${scriptDir}\\input\\`;
const output_dir = `${scriptDir}\\output\\`;
const output = `${COMBINE_NAME}.txt`;
const output_simple = `${COMBINE_NAME}_Simple.txt`;
const SHOW_FILE_LINE = true; // 必须为 true 才能获取文件行号生成注释
const IS_GEN_SIMPLE_STR = true;
let strings = new Set();
let strings_simple = new Set();
const tempJson = {};

function insertJson(tableId, srcTxt, replaceFullPath, line, type, tgtTxt, args = null) {
    tempJson[tableId] = {
        srcTxt: srcTxt,
        path: replaceFullPath,
        line: line,
        type: type,
        tgtTxt: tgtTxt,
    }
    if (args) {
        tempJson[tableId]['args'] = args;
    }
}
//single 解析 AST
function traverse(filePath) {
    const content = fs.readFileSync(filePath, IO_ENCODING);
    try {
        const ast = parse(content, {
            ecmaVersion: getValueByKey("ecmaVersion"),
            sourceType: getValueByKey("sourceType"),
            loc: true,
            range: true
        });
        traverseAST(ast, filePath, content); // 递归遍历 AST 节点
    } catch (e) {
        console.warn(`Parse error in ${filePath}`);
    }
}
function isStaticProperty(node) {
    if(getValueByKey("isReplaceStaticProperty")){
        return false; //要替换静态的时候,当做不是静态属性即可
    }
    if (node.type === 'PropertyDefinition' && node.static) {
        // if(node.key && node.key.type == "Identifier"){
        //     console.log(`忽略属性:${node.accessibility} static ${node.key.name}`);
        // }
        return true;
    }
    return false;
}
// 递归AST节点,查找中文字符串
function traverseAST(node, file, content) {
    if(isStaticProperty(node)){
        return;
    }
    if (node.type === 'Literal' &&
        typeof node.value === 'string' &&
        /[\u4e00-\u9fa5]/.test(node.value)) {
        handleStringLiteral(node, file, content, 0);
    }
    if (node.type === 'TemplateLiteral') {
        let processedStr = '';
        let argIndex = 0; // 参数索引计数器

        for (let i = 0; i < node.quasis.length; i++) {
            const quasi = node.quasis[i];
            processedStr += quasi.value.raw;
            if (i < node.quasis.length - 1) {
                processedStr += `{${argIndex++}}`; // 递增索引
            }
        }

        if (/[\u4e00-\u9fa5]/.test(processedStr)) {
            handleStringLiteral(/* {
                value: processedStr,
                loc: node.loc // 使用模板字符串整体位置
            } */node, file, content, 1, processedStr);
        }
    }
    for (let key in node) {
        if (node.hasOwnProperty(key) &&
            typeof node[key] === 'object' &&
            node[key] !== null) {
            traverseAST(node[key], file, content); // dfs
        }
    }
}

function handleStringLiteral(node, file, content, type/* 1有processedStr */, processedStr = null) {
    if (!node.loc) {
        console.error(`未知行,不予记录,跳过; file=>${file}`)
        return;
    }
    const newTblId = tblStartId++;
    const line = node.loc.start.line;
    const isLiterStringType = isLiteral(type);
    let srcTxt = "";
    let args = [];
    if(isLiterStringType){
        srcTxt = node.raw;
        args = null;
    }else{
        srcTxt = content.slice(node.range[0], node.range[1]);
        node.expressions.forEach(expr => {
            const [start, end] = expr.range;
            args.push(content.slice(start, end)); 
        });
    }

    const uid = `${__Mark}${newTblId}`;
    const tblSaveStr = isLiterStringType ? node.value : processedStr;
    insertJson(newTblId, srcTxt, file, line, type, tblSaveStr, args)
    if (SHOW_FILE_LINE) {
        // const line = node.loc ? node.loc.start.line : '未知行';
        strings.add(`${file}:${line}${uid}\n${/* node.value */tblSaveStr}`);
        // console.log(`enterCount-------------------->${testEnterCount}|||${strings.size}`)
    }

    if (IS_GEN_SIMPLE_STR) {
        strings_simple.add(`${/* node.value */tblSaveStr}${uid}`);
        // console.log(`enterCount-------------------->${testEnterCount}|||${strings_simple.size}`)
    }
}

function isLiteral(type){
    return type == 0;
}

function walkDir(currentPath) {
    if (!fs.existsSync(currentPath)) {
        console.error(`路径不存在: ${currentPath}`);
        return;
    }

    const files = fs.readdirSync(currentPath);

    for (const file of files) {
        const fullPath = path.join(currentPath, file);
        const stat = fs.statSync(fullPath);
        if (stat.isDirectory()) {
            if(ignore_dir.indexOf(file) > -1){
                console.log(`==========忽略文件夹:${file}`)
            }else{
                walkDir(fullPath);
            }
        } else if (file.endsWith(POSTFIX_TS)) {
            if(ignore_file.indexOf(file) > -1){
                console.log(`==========忽略文件:${file}`)
            }else{
                traverse(fullPath);
            }
        } else {

        }
    }
}


function generateEnumFile() {
    const entries = Array.from(strings).map(entry => {
        const [fileLine, value] = entry.split('\n');
        const lastColonIndex = fileLine.lastIndexOf(':');
        return {
            file: fileLine.slice(0, lastColonIndex),
            line: disposeUIdStrByReg(fileLine.slice(lastColonIndex + 1)),
            value: value
        };
    });

    const enumLines = entries.map(({ file, line, value }, index) => {
        const id = index + ENUM_START_ID;
        const key = `ID_${id}`;
        const fileName = path.basename(file); // 提取文件名(去除路径)
        // 使用 JSON.stringify 自动转义特殊字符(如 "、\n 等)
        return `\t// ${fileName}:${line}\n\t${key} = ${id}, //${JSON.stringify(value)}`;
    });
    const enumContent = `/** Attention! The annotations will not change along with the actual table. */\nexport enum ${COMBINE_NAME} {\n${enumLines.join('\n')}\n}`;
    fs.writeFileSync(output_dir + CharConstTSFile, enumContent, IO_ENCODING);
}
const enableDelOutput = true;
const delOutputTipsShow = false;

async function clearOutputDir() {
    const dirPath = output_dir;
    if (!enableDelOutput) return;
    console.log(`enableDelOutput:${output_dir}`)

    if (!fs.existsSync(dirPath)) {
        console.log(`目录不存在: ${dirPath}`);
        return;
    }

    // 创建 readline 接口
    const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout
    });

    try {
        // 等待用户确认
        let rst = "y";
        if (delOutputTipsShow) {
            rst = await new Promise(resolve => {
                rl.question(`即将清空目录 ${dirPath},确认继续吗?(y/n) `, resolve);
            });
        }
        if (rst.trim().toLowerCase() === 'y') {
            // 执行删除操作
            fs.rmSync(dirPath, { recursive: true, force: true });
            fs.mkdirSync(dirPath);
            console.log('√ 目录已清空');
        } else {
            console.log('× 操作已取消');
        }
    } finally {
        rl.close();
    }
}

function disposeUIdStrByReg(s) {
    return s.replace(/________\d+/, '');
}
async function main() {
    try {
        await clearOutputDir();
        load_config();
        init_cfg_value();
        walkDir(input_dir);
        fs.writeFileSync(
            path.join(output_dir, output),
            Array.from(strings).map(s => disposeUIdStrByReg(s)).join('\n'),
            IO_ENCODING
        );
        fs.writeFileSync(path.join(output_dir, output_simple), Array.from(strings_simple).map(str => str.split(__Mark)[0]).join('\n'), IO_ENCODING);
        const genTsEnum = getValueByKey("genTsEnum");
        if(genTsEnum){
            generateEnumFile();
        }
        console.log(`
        √ 提取完成:
           - 找到 strings.size=>${strings.size}|||strings_simple.size=>${strings_simple.size} 条中文字符串(已写入 ${output}${output_simple}${genTsEnum ? "- 生成枚举文件 ${CharConstTSFile}" : ""}
        `);
        let jsonData;
        if(getValueByKey("isPrettyJsn")){
            jsonData = JSON.stringify(tempJson, null, 2);
        }else{
            jsonData = JSON.stringify(tempJson);
        }
        fs.writeFileSync(path.join(output_dir, getValueByKey("tempJ")), jsonData);
    } catch (err) {
        console.error('× 运行出错:', err);
        process.exit(1);
    }
}
main();


let json_obj;
let ignore_file;
let ignore_dir;
function init_cfg_value(){
    tblStartId = ENUM_START_ID = getValueByKey("start_id");
    ignore_file = getValueByKey("ignore_file") || [];
    ignore_dir = getValueByKey("ignore_dir") || [];
}
function load_config(){
    try {
        const data = fs.readFileSync('./config.json', 'utf8');
        json_obj = JSON.parse(data);
    } catch (err) {
        console.error('config.json解析失败:', err);
        process.exit(1);
    }
}

function getValueByKey(key) {
    if (json_obj && key in json_obj) {
        return json_obj[key];
    }
    return null;
}



替换代码中文

// tool_replacer.py
import json
import os
import re
from pathlib import Path
import logging
import os



def setup_logger():
    log_dir = "output"
    os.makedirs(log_dir, exist_ok=True)
    log_file = os.path.join(log_dir, "log.txt")

    logger = logging.getLogger("my_logger")
    logger.setLevel(logging.INFO)

    file_handler = logging.FileHandler(log_file, encoding="utf-8", mode="a")
    file_handler.setFormatter(logging.Formatter('[%(asctime)s] %(message)s'))
    logger.addHandler(file_handler)

    console_handler = logging.StreamHandler()
    console_handler.setFormatter(logging.Formatter('%(message)s'))
    logger.addHandler(console_handler)

    return logger


class TsTextReplacer:
    def __init__(self, config_path="config.json", data_path="output/tempJ.json"):
        self.config = self.load_config(config_path)
        self.json_data = self.load_data(data_path)
        str_cfg=self.config['funCls']
        pattern = r'^import\s+.*?\b{}\b'.format(re.escape(str_cfg))
        self.import_pattern = re.compile(pattern, re.MULTILINE)
        str_import_cfg=self.config['importAbsolute']
        self.import_statement = f"{str_import_cfg}\n"

    def load_config(self, path):
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)

    def load_data(self, path):
        with open(path, 'r', encoding='utf-8') as f:
            return json.load(f)

    # 接收键和项作为参数
    def generate_replacement(self, key, item):
        base = f"{self.config['replaceFunc']}({key}"
        if item.get('type', 0)!= 0 and item.get('args'):
            args = ', '.join(item['args'])
            return f"{base}, {args})"
        return f"{base})"

    def add_import_statement(self, content):
        if not self.import_pattern.search(content):
            return self.import_statement + content
        return content

    def process_file(self, file_path, line, src_txt, replacement):
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.readlines()
            modified = False
            for i in range(line - 1, len(content)):
                if src_txt in content[i]:
                    content[i] = content[i].replace(src_txt, replacement, 1)
                    modified = True
                    break
            if modified:
                full_content = ''.join(content)
                full_content = self.add_import_statement(full_content)
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(full_content)
                return True
            return False
        except Exception as e:
            logger.info(f"处理文件 {file_path} 失败: {str(e)}")
            return False

    def run(self):
        if self.config['replacer_exe']:
            success_count = 0
            for key, item in self.json_data.items():
                if not all(k in item for k in ['srcTxt', 'path', 'line']):
                    continue
                replacement = self.generate_replacement(key, item)
                file_path = Path(item['path']).resolve()
                if not file_path.exists():
                    # print(f"文件不存在: {file_path}")
                    logger.info(f"文件不存在: {file_path}")
                    continue
                if self.process_file(file_path, item['line'], item['srcTxt'], replacement):
                    # print(f"已处理: {file_path} [行{item['line']}]")
                    logger.info(f"已处理: {file_path} [行{item['line']}]")
                    success_count += 1
                else:
                    # print(f"未找到匹配: {file_path} [行{item['line']}]")
                    logger.info(f"未找到匹配: {file_path} [行{item['line']}]")
            # print(f"\n处理完成!成功替换 {success_count}/{len(self.json_data)} 处")
            endLog = f"************处理完成!成功替换 {success_count}/{len(self.json_data)} 处"
            logger.info(f"{endLog}")
            escape_info_str(endLog)
        else:
            endLog = "************跳过了替换代码中文步骤!"
            logger.info(f"{endLog}")
            escape_info_str(endLog)

def escape_info_str(value):
    escaped_value = value.replace("%", "%%")
    print(f'echo "{escaped_value}"')

if __name__ == "__main__":
    logger = setup_logger()
    replacer = TsTextReplacer()
    replacer.run()

生成Excel

// tool_excel_generator.py
import json
import os
from pathlib import Path
from openpyxl import Workbook
from openpyxl.utils import get_column_letter

class ExcelGenerator:
    def __init__(self):
        self.config = self.load_config()
        self.json_data = self.load_data()
        self.output_dir = Path("output")
        self.output_dir.mkdir(exist_ok=True)
    # ed
    def load_config(self):
        with open("config.json", "r", encoding="utf-8") as f:
            config = json.load(f)
        return {
            "xls_name": config.get("xls_name", "lang") + ".xls",
            "columns": ["NOTE", "ID", "文本", "空列", "代码文件来源", "被替换的源代码"]
        }
    # ed
    def load_data(self):
        with open("output/tempJ.json", "r", encoding="utf-8") as f:
            return json.load(f)
    # ed
    def get_filename_from_path(self, path):
        return Path(path).stem
    # ed
    def clean_output_xls_rtn_name(self):
        output_path = self.output_dir / self.config["xls_name"]
        if output_path.exists():
            os.remove(output_path)
        return output_path

    # ed
    def generate_excel(self):
        if self.config["gen_xls_exe"]:
            """生成Excel文件"""
            output_path = self.clean_output_xls_rtn_name()
            
            wb = Workbook()
            ws = wb.active
            ws.title = "Lang Data"
            
            ws.column_dimensions[get_column_letter(2)].width = 15  # Key列
            ws.column_dimensions[get_column_letter(3)].width = 40  # tgtTxt列
            ws.column_dimensions[get_column_letter(5)].width = 35  # Path:Line列
            ws.column_dimensions[get_column_letter(6)].width = 60  # srcTxt列

            # 写入标题行
            ws.append(self.config["columns"])

            # 写入数据
            row_counter = 0
            for idx, (key, item) in enumerate(sorted(self.json_data.items(), key=lambda x: int(x[0])), 1):
                filename = self.get_filename_from_path(item["path"])
                path_line = f"{filename}:{item['line']}"
                
                row = [
                    f"VALUE",
                    key,
                    item.get("tgtTxt", ""),
                    "",
                    path_line,
                    item.get("srcTxt", "")
                ]
                
                ws.append(row)
                row_counter += 1

                # 每20行插入空行
                if row_counter % 20 == 0:
                    ws.append([""]*len(self.config["columns"]))

            # 保存文件
            wb.save(output_path)
            print(f"生成成功!文件位置:{output_path}")
        else:
            print(f"跳过了生成xls文件步骤!")

            
if __name__ == "__main__":
    generator = ExcelGenerator()
    generator.generate_excel()

启动脚本

REM zstartup.bat
@echo off
echo start tool_chinese_strings_extracted...
node tool_chinese_strings_extracted.js
if errorlevel 1 (
    echo tool_chinese_strings_extracted fail.
    pause
    exit /b
)

echo start tool_replacer...
REM start tool_replacer.exe
call tool_replacer.exe > temp___vars.bat
call temp___vars.bat
del temp___vars.bat
if errorlevel 1 (
    echo tool_replacer fail.
	start log_show.bat
    pause
    exit /b
)

echo start tool_excel_generator...
start tool_excel_generator.exe
if errorlevel 1 (
    echo tool_excel_generator fail.
	start log_show.bat
    pause
    exit /b
)
call log_show.bat
echo success...
pause

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2405184.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pgsql batch insert optimization (reWriteBatchedInserts )

reWriteBatchedInserts 是 PostgreSQL JDBC 驱动 提供的一个优化选项&#xff0c;它可以 重写批量插入语句&#xff0c;从而提高插入性能。 作用 当 reWriteBatchedInsertstrue 时&#xff0c;PostgreSQL JDBC 驱动会将 多个单独的 INSERT 语句 转换为 一个多行 INSERT 语句&a…

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(上)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…

华为云Flexus+DeepSeek征文 | 基于DeepSeek-V3构建企业知识库问答机器人实战

作者简介 我是摘星&#xff0c;一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型&#xff0c;将实际使用经验分享给大家&#xff0c;希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 1. 引言 2. 技术选型与架构设计 2.1 技…

【Docker 01】Docker 简介

&#x1f308; 一、虚拟化、容器化 ⭐ 1. 什么是虚拟化、容器化 物理机&#xff1a;真实存在的服务器 / 计算机&#xff0c;对于虚拟机来说&#xff0c;物理机为虚拟机提供了硬件环境。虚拟化&#xff1a;通过虚拟化技术将一台计算机虚拟为 1 ~ n 台逻辑计算机。在一台计算机…

AUTOSAR实战教程--DoIP_02_诊断链路建立流程

第一步&#xff1a;DoIP实体车辆声明/诊断仪车辆识别请求 打开激活线以后&#xff0c;DoIP实体发的三帧车辆声明报文。其中包含了DoIP实体的诊断逻辑地址&#xff08;可以类比DoCAN的物理请求/响应地址&#xff09;&#xff0c;对应车辆的VIN码&#xff08;若已配置&#xff0…

音频剪辑软件少之又少好用

我们平时见到的图片以及视频编辑工具非常多&#xff0c;但是音频剪辑软件却是少之又少&#xff0c;更不用说有没有好用的&#xff0c;今天&#xff0c;给大家带来一款非常专业的音频剪辑软件&#xff0c;而且是会员喔。 软件简介 一款手机号登录即可以享受会员的超专业音频剪…

客户端和服务器已成功建立 TCP 连接【输出解析】

文章目录 图片**1. 连接状态解析****第一条记录&#xff08;服务器监听&#xff09;****第二条记录&#xff08;客户端 → 服务器&#xff09;****第三条记录&#xff08;服务器 → 客户端&#xff09;** **2. 关键概念澄清****(1) 0.0.0.0 的含义****(2) 端口号的分配规则** *…

day26-计算机网络-4

1. tcp的11种状态 ss -ant -a 表示看所有状态 -n 表示不将ip解析为主机名 -t 表示tcp 1.1. closed状态&#xff08;客户端、服务端&#xff09; 客户端发起建立连接前的状态服务端启动服务前的状态 1.2. listen状态&#xff08;服务端&#xff09; 服务端软件运行的时候状…

国防科技大学计算机基础慕课课堂学习笔记

1.信息论 香农作为信息论的这个创始人&#xff0c;给出来了这个信息熵的计算方法&#xff0c;为我们现在的这个生活的很多领域奠定了基础&#xff0c;我第一次听说这个信息熵是在这个数学建模里面的理论学习中有关于这个&#xff1a;决策树的模型&#xff0c;在那个问题里面&a…

【第七篇】 SpringBoot项目的热部署

简介 本文介绍了热部署&#xff08;Hot Deployment&#xff09;的概念、使用场景及在IDEA中的配置方法。热部署可在不重启应用的情况下动态更新代码&#xff0c;提升开发效率&#xff0c;适用于调试、微服务架构和自动化测试等场景。文章详细说明了热部署的实现步骤&#xff08…

解决pycharm同一个文件夹下from *** import***仍显示No module named

1、&#xff0c;from ***import *&#xff0c;同文件夹中已有.py文件但是仍然报错No module named 原因是因为pycharm没有把文件夹设置为根目录&#xff0c;只需要在文件夹的上一级设置为根目录即可&#xff0c;测试过如果仅仅将当前的文件夹设置仍然报错&#xff0c;如果把最上…

使用 Redisson 实现分布式锁—解决方案详解

Redisson 是 Redis 官方推荐的 Java 客户端&#xff0c;提供了一系列分布式服务实现&#xff0c;其中分布式锁是其核心功能之一。本文将深入解析 Redisson 分布式锁的实现原理、高级特性和最佳实践。 一、Redisson 分布式锁的优势 与传统实现的对比 特性手动实现Redisson 实现…

结合三维基因建模与智能体技术打造工业软件无码平台

通过深度整合 Protocol Buffers (Protobuf)、gRPC 及 Microsoft AI 技术&#xff0c;构建面向智能制造的高性能、智能化 PLM 平台。 一、Protocol Buffers 深度集成 1. 基因模型标准化定义 三维基因容器 Protobuf 规范&#xff1a; protobuf syntax "proto3"; pa…

Python Day46

Task&#xff1a; 1.不同CNN层的特征图&#xff1a;不同通道的特征图 2.什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。 3.通道注意力&#xff1a;模型的定义和插入的位置 4.通道注意力后的特征图和热力…

基于PostGIS的各地级市路网长度统计及Echarts图表可视化实践-以湖南省为例

目录 前言 一、路网长度计算 1、地级市列表查询 2、地级市路网长度查询 二、Echarts可视化实现 1、Echarts后端生成 2、引入Colormap配色 3、前端微调 三、总结 前言 在当今快速发展的社会中&#xff0c;交通路网的建设与布局对于一个地区的经济发展、居民生活以及城市…

mac版excel如何制作时长版环形图

设置辅助列 创建簇状柱形图 将辅助列绘制在次坐标轴 工作时长在主坐标轴&#xff0c;右键分别更改图表类型为圆环。 辅助列圆环全部为灰色&#xff0c;边框为白色 辅助列设置透明度100% 设置辅助列和工作时长列同样的圆环大小 可得 核心&#xff1a;只要辅助列边框不透明…

【MySQL系列】MySQL 执行 SQL 文件

博客目录 一、MySQL 执行 SQL 文件的常见场景二、MySQL 执行 SQL 文件的主要方法1. 使用 MySQL 命令行客户端2. 在 MySQL 交互界面中使用 source 命令3. 使用 MySQL Workbench 等图形化工具4. 使用编程语言接口 三、执行 SQL 文件时的注意事项1. 字符集问题2. 事务处理3. 错误处…

论文MR-SVD

每个像素 7 个 FLOPs意思&#xff1a; FLOPs&#xff08;浮点运算次数&#xff09;&#xff1a;衡量算法计算复杂度的指标&#xff0c;数值越小表示运算越高效。含义&#xff1a;对图像中每个像素进行处理时&#xff0c;仅需执行7 次浮点运算&#xff08;如加减乘除等&#xf…

Java 日期时间类全面解析

Java 日期时间类全面解析&#xff1a;从传统到现代的演进 一、发展历程概览 二、传统日期类&#xff08;Java 8前&#xff09; 1. java.util.Date - 日期表示类 Date now new Date(); // 当前日期时间 System.out.println(now); // Wed May 15 09:30:45 CST 2023// 特定时间…

【工具-Wireshark 抓包工具】

工具-Wireshark 抓包工具 ■ Wireshark 抓包工具■ 通过IP指定查看■■ ■ Wireshark 抓包工具 抓包工具】win 10 / win 11&#xff1a;WireShark 下载、安装、使用 Wireshark下载 阿里云镜像 ■ 通过IP指定查看 ■ ■