Commit ef7b6034 authored by wanglei's avatar wanglei

[打包]替换脚本

parent 1db89a5e
......@@ -13,6 +13,10 @@ import regex as re2
# import chardet
import string
import random
import nltk
import sys
from nltk.corpus import words
def detect_encoding(file_path):
......@@ -300,9 +304,17 @@ def sub_map_text_impl(mapping, result, skip_type, skip_name, is_reverse=True, pa
reverse = sorted(list(mapping.keys()), key=len, reverse=is_reverse)
text_result = text
# 遍历映射规则替换
for name in reverse:
text_result = re2.sub(str(name), mapping[name], text_result)
patterns = sorted(mapping.keys(), key=len, reverse=is_reverse)
for pattern in patterns:
try:
print(f"正在替换 pattern: {pattern}")
text_result = re.sub(pattern, mapping[pattern], text_result)
except Exception as e:
print(f"替换失败: pattern={pattern}, 错误: {e}")
# # 遍历映射规则替换
# for name in reverse:
# text_result = re2.sub(str(name), mapping[name], text_result)
# 方案 3:针对特定 import 的直接替换
text_result = re.sub(
......@@ -400,7 +412,8 @@ def check_class_mapping():
mapping = {}
classes = get_classes()
for i in classes:
if '_D' in i or os.path.basename(i) == "GlobalConfig.kt": # 跳过 GlobalConfig.kt
#or os.path.basename(i) == "GlobalConfig.kt" 去除跳过GlobalConfig
if '_D' in i : # 跳过 GlobalConfig.kt
continue
print(i)
name = os.path.basename(i)
......@@ -510,13 +523,65 @@ def deal_ids_type(ids_type):
sub_map = {}
for i in type_ids_mapping:
if i in type_ids_mapping and type_ids_mapping[i].startswith("android"):
continue
# mapped = type_ids_mapping[i]
#
# sub_map['<' + ids_type + ' name="' + i + '"'] = '<' + ids_type + ' name="' + mapped + '"'
# sub_map['<item name="' + i + '"(.*? type="' + ids_type + '")>'] = '<item name="' + mapped + '"\\g<1>>'
# sub_map[r'(?<=[^\.])R\.' + ids_type + r'\.' + re.escape(i) + r'(?=\W)'] = 'R.' + ids_type + '.' + mapped
# sub_map[r'(?<=>|")@' + ids_type + '/' + re.escape(i) + r'(?=<|")'] = '@' + ids_type + '/' + mapped
# sub_map[r'(?<=[@=:])@' + ids_type + '/' + re.escape(i) + r'(?=[}"\'\s])'] = '@' + ids_type + '/' + mapped
#
# # ✅ DataBinding 表达式中所有 @xxx/yyy(包括拼接字符串等复杂表达式)
# sub_map[r'(?<=@{[^}]*?)@' + ids_type + r'/' + re.escape(i) + r'(?=[^}]*})'] = '@' + ids_type + '/' + mapped
#
# # ✅ <style name="..." parent="xxx"> 资源引用
# sub_map[r'<' + ids_type + r'(.*?) parent="' + re.escape(i) + r'"(.*?)>'] = (
# r'<' + ids_type + r'\g<1> parent="' + mapped + r'"\g<2>>'
# )
sub_map['<' + ids_type + ' name="' + i + '"'] = '<' + ids_type + ' name="' + type_ids_mapping[i] + '"'
sub_map['<item name="' + i + '"(.*? type="' + ids_type + '")>'] = \
'<item name="' + type_ids_mapping[i] + '"\\g<1>>'
sub_map['<item name="' + i + '"(.*? type="' + ids_type + '")>'] = '<item name="' + type_ids_mapping[
i] + '"\\g<1>>'
sub_map['(?<=[^\\.])R\\.' + ids_type + '\\.' + i + '(?=\\W)'] = 'R.' + ids_type + '.' + type_ids_mapping[i]
sub_map['(?<=>|")@' + ids_type + '/' + i + '(?=<|")'] = '@' + ids_type + '/' + type_ids_mapping[i]
sub_map['<' + ids_type + '(.*?) parent="' + i + '"(.*?)>'] = \
'<' + ids_type + '\\g<1> parent="' + type_ids_mapping[i] + '"\\g<2>>'
sub_map[r'(?<=[@=:])@' + ids_type + '/' + i + r'(?=[}"\'\s])'] = '@' + ids_type + '/' + type_ids_mapping[i]
sub_map[r'(?<=@{[^}]*?)@' + ids_type + r'/' + i + r'(?=[^}]*})'] = '@' + ids_type + '/' + type_ids_mapping[
i] # ⬅ 适配 data binding 表达式
sub_map['<' + ids_type + '(.*?) parent="' + i + '"(.*?)>'] = '<' + ids_type + '\\g<1> parent="' + \
type_ids_mapping[i] + '"\\g<2>>'
escaped_i = re.escape(i)
sub_map[rf'(?<![a-zA-Z0-9_/])@{ids_type}/{escaped_i}(?![a-zA-Z0-9_])'] = f'@{ids_type}/{type_ids_mapping[i]}'
# 带包名前缀的资源路径 com.retrytech.ledgeapp.R.color.xxx
# sub_map[rf'(\b[\w\.]+)\.R\.{ids_type}\.{re.escape(i)}\b'] = rf'\1.R.{ids_type}.{type_ids_mapping[i]}'
# sub_map[
# rf'(?<!android\.)(\b[\w\.]+)\.R\.{ids_type}\.{re.escape(i)}\b'] = rf'\1.R.{ids_type}.{type_ids_mapping[i]}'
sub_map[
rf'(?<!\bandroid\.)\b([\w\.]+)\.R\.{ids_type}\.{re.escape(i)}\b'] = rf'\1.R.{ids_type}.{type_ids_mapping[i]}'
# ✅ 推荐的替换逻辑(万能适配 DataBinding 场景)
# sub_map[f'@{ids_type}/{i}'] = f'@{ids_type}/{type_ids_mapping[i]}'
# # ✅ 适配 DataBinding 表达式中包括拼接、三目等形式的字符串资源引用
# sub_map[rf'(?<=@{{[^}}]*?)@{ids_type}/{re.escape(i)}(?=[^}}]*}})'] = f'@{ids_type}/{type_ids_mapping[i]}'
# # DataBinding 表达式中字符串拼接,如:@{@string/subscribe_to+" "+@string/pro}
# sub_map[r'(?<=@{[^}]*?)@' + ids_type + r'/' + re.escape(i) + r'(?=[^}]*})'] = '@' + ids_type + '/' + \
# type_ids_mapping[i]
# sub_map['<' + ids_type + ' name="' + i + '"'] = '<' + ids_type + ' name="' + type_ids_mapping[i] + '"'
# sub_map['<item name="' + i + '"(.*? type="' + ids_type + '")>'] = \
# '<item name="' + type_ids_mapping[i] + '"\\g<1>>'
# sub_map['(?<=[^\\.])R\\.' + ids_type + '\\.' + i + '(?=\\W)'] = 'R.' + ids_type + '.' + type_ids_mapping[i]
# sub_map['(?<=>|")@' + ids_type + '/' + i + '(?=<|")'] = '@' + ids_type + '/' + type_ids_mapping[i]
# sub_map['(?<=[@=:])@' + ids_type + '/' + i + r'(?=[}"\'\s])'] = '@' + ids_type + '/' + type_ids_mapping[i]
# sub_map['<' + ids_type + '(.*?) parent="' + i + '"(.*?)>'] = \
# '<' + ids_type + '\\g<1> parent="' + type_ids_mapping[i] + '"\\g<2>>'
# # 匹配 @color/xxx 出现在 @{} 表达式中,适配 data binding 多种情况
# sub_map[r'(?<=@{[^}]*?)@' + ids_type + r'/' + i + r'(?=[^}]*})'] = '@' + ids_type + '/' + type_ids_mapping[i]
sub_map[
rf'android\.R\.color\.(\w+)'] = lambda m: 'android.R.color.' + type_ids_mapping.get(m.group(1), m.group(1))
sub_map_text(sub_map)
......@@ -675,7 +740,9 @@ def replace_package_in_imports(root_path, old_package, new_package):
patterns = [
(re.compile(rf'import {re.escape(old_package)}(\.databinding\.\w+)'), rf'import {new_package}\1'),
(re.compile(rf'import {re.escape(old_package)}\.R\b'), rf'import {new_package}.R'),
(re.compile(rf'import {re.escape(old_package)}\.BuildConfig'), rf'import {new_package}.BuildConfig')
(re.compile(rf'import {re.escape(old_package)}\.BuildConfig'), rf'import {new_package}.BuildConfig'),
(re.compile(rf'{re.escape(old_package)}\.R\b'), rf'{new_package}.R'),
]
# 遍历目录中的所有文件
......@@ -729,7 +796,6 @@ def replace_package_in_imports(root_path, old_package, new_package):
# return re.findall(class_pattern, text)
def get_method_names(text, filename):
"""
获取 Java 或 Kotlin 文件中的所有方法名
......@@ -749,6 +815,7 @@ def get_method_names(text, filename):
# 匹配所有方法名
return re.findall(method_pattern, text)
def get_variable_names(text, filename):
"""
获取 Java 或 Kotlin 文件中的所有变量名
......@@ -768,6 +835,7 @@ def get_variable_names(text, filename):
# 匹配所有变量名
return re.findall(variable_pattern, text)
def get_file_names_without_extension(root_path):
"""
获取所有 `.kt` 和 `.java` 文件的路径(无扩展名)
......@@ -785,6 +853,7 @@ def get_file_names_without_extension(root_path):
# 存储每个文件名的映射关系
file_to_class_map = {}
def get_constant_names(text, filename):
"""
获取 Java 或 Kotlin 文件中的所有常量名
......@@ -910,9 +979,6 @@ def replace_with_mapped_names(all_files, name_mapping, constant_mapping):
print(f"文件 {file_path} 引用的名称已进行统一混淆处理。")
def deal_code():
print("deal_code start")
# 替换点击事件
......@@ -959,9 +1025,6 @@ def deal_code():
with open(get_path('.\\src\\main\\AndroidManifest.xml'), 'w', encoding='utf-8') as f:
f.write(text)
# 修改依赖
sub_map = {}
for i in mapping:
......@@ -992,7 +1055,8 @@ def deal_code():
classes = get_classes()
print(class_mapping)
for i in classes:
if '_D' in i or os.path.basename(i) == "GlobalConfig.kt": # 跳过 GlobalConfig.kt
#"or os.path.basename(i) == "GlobalConfig.kt" 去除跳过 GlobalConfig.kt
if '_D' in i :
continue
name = os.path.basename(i)
name = name[:name.rfind('.')]
......@@ -1005,6 +1069,12 @@ def deal_code():
# 添加排除前缀 "styleable." 的逻辑
# 原规则匹配整个单词,添加排除前缀 "styleable." 的逻辑,并允许后面有引号的匹配
regex_pattern = r'(?<!")(?<!\bstyleable\.)\b' + i + r'\b'
if 'AppComponent' in class_mapping:
obfuscated_name = class_mapping['AppComponent']
dagger_pattern = rf'(?<!")(?<!\bstyleable\.)\bDaggerAppComponent\b'
sub_map[dagger_pattern] = f'Dagger{obfuscated_name}'
else:
print("⚠️ class_mapping 中没有 AppComponent,跳过 DaggerAppComponent 规则")
# 保持原有规则的匹配,优先处理非引号的情况
sub_map[regex_pattern] = class_mapping[i]
......@@ -1012,7 +1082,6 @@ def deal_code():
sub_map_text(sub_map)
print("类名 over")
# 混淆字典
check_obfuscation_dictionary()
print(root_path)
......@@ -1039,7 +1108,6 @@ def deal_code():
# # 第二步:替换其他文件中的引用,保证同样的混淆名
# replace_with_mapped_names(all_files, name_mapping, constant_mapping)
replace_package_in_imports(root_path, mapping[applicationId], applicationId)
......@@ -1079,8 +1147,45 @@ def deal_res_type(res_type):
sub_map = {}
for i in type_mapping:
sub_map['R\\.' + res_type + '\\.' + i + '(\\W)'] = 'R.' + res_type + '.' + type_mapping[i] + '\\g<1>'
sub_map['(>|")@' + res_type + '/' + i + '(<|")'] = '\\g<1>@' + res_type + '/' + type_mapping[i] + '\\g<2>'
mapped = type_mapping[i]
# 普通 R.xxx.xxx
sub_map[r'R\.' + res_type + r'\.' + re.escape(i) + r'(\W)'] = r'R.' + res_type + '.' + mapped + r'\1'
# # # 包名限定的 com.retrytech.ledgeapp.R.drawable.xxx
# sub_map[r'(\b[\w\.]+)\.R\.' + res_type + r'\.' + re.escape(
# i) + r'(\W)'] = r'\1.R.' + res_type + '.' + mapped + r'\2'
# sub_map[r'(\b[\w\.]+)\.R\.' + res_type + r'\.' + re.escape(
# i) + r'(?=[\s)\];,])'] = r'\1.R.' + res_type + '.' + mapped
# ✅ 推荐统一写法:以单词边界结尾,适配更多结尾符号(避免重复)
sub_map[rf'(\b[\w\.]+)\.R\.{res_type}\.{re.escape(i)}\b'] = rf'\1.R.{res_type}.{mapped}'
# XML 资源:@drawable/xxx
sub_map[r'(>|")@' + res_type + r'/' + re.escape(i) + r'(<|")'] = r'\1@' + res_type + '/' + mapped + r'\2'
# DataBinding 表达式中:@{ condition ? @drawable/xxx :@drawable/yyy }
# # 匹配 ? 后的资源
# sub_map[r'(?<=\? *@' + res_type + r'\/)' + re.escape(i) + r'(?= *:)'] = mapped
# # 匹配 : 后的资源
# sub_map[r'(?<=: *@' + res_type + r'\/)' + re.escape(i) + r'(?= *})'] = mapped
# ? 后的资源
sub_map[r'(?<=\? *@' + res_type + r'/)' + re.escape(i) + r'(?=\b)'] = mapped
# : 后的资源
sub_map[r'(?<=: *@' + res_type + r'/)' + re.escape(i) + r'(?=\b)'] = mapped
# ✅ 通配 DataBinding 内的任意资源引用
sub_map[r'(@\{[^}]*?)@' + res_type + r'/' + re.escape(i) + r'(?=[^}]*})'] = r'\1@' + res_type + '/' + mapped
# 通用 DataBinding 表达式中任意位置(不拆三元)
# sub_map[r'(?<=@{[^}]*?)@' + res_type + r'/' + re.escape(i) + r'(?=[^}]*})'] = '@' + res_type + '/' + mapped
# sub_map['R\\.' + res_type + '\\.' + i + '(\\W)'] = 'R.' + res_type + '.' + type_mapping[i] + '\\g<1>'
# sub_map[
# r'(\b[\w\.]+)\.R\.' + res_type + r'\.' + i + r'(\W)'
# ] = r'\1.R.' + res_type + '.' + type_mapping[i] + r'\2'
#
# sub_map['(>|")@' + res_type + '/' + i + '(<|")'] = '\\g<1>@' + res_type + '/' + type_mapping[i] + '\\g<2>'
# # 适配 DataBinding 表达式中的 @drawable/xxx 写法
# sub_map[r'(?<=@{[^}]*?)@' + res_type + r'/' + i + r'(?=[^}]*})'] = '@' + res_type + '/' + type_mapping[i]
sub_map_text(sub_map)
......@@ -1478,7 +1583,7 @@ def deal_res():
'R.styleable.' + styleable_mapping[styleable_id] + '_' + styleable_mapping[attr + '_A'] + '\\g<1>'
sub_map['app:' + attr + '='] = 'app:' + styleable_mapping[attr + '_A'] + '='
sub_map['\\?attr/' + attr] = '?attr/' + styleable_mapping[attr + '_A'] # 新增的替换规则
sub_map['<item name=\"' + attr +'\"'] = '<item name=\"' + styleable_mapping[attr + '_A'] +'\"' # 新增的替换规则
sub_map['<item name=\"' + attr + '\"'] = '<item name=\"' + styleable_mapping[attr + '_A'] + '\"' # 新增的替换规则
# 判断 attr 是否以 "android:" 开头
if attr.startswith("android:"):
new_attr = "android_" + attr[8:] # 去掉 "android:" 并替换为 "android_"
......@@ -1500,9 +1605,28 @@ def deal_res():
sub_map['(>|")@(\\+)?id/' + i + '(<|")'] = '\\g<1>@\\g<2>id/' + view_ids_mapping[key] + '\\g<3>'
sub_map['R\\.id\\.' + i + '(\\W)'] = 'R.id.' + view_ids_mapping[key] + '\\g<1>'
sub_map['([bB]inding\\??(?:\\.\\w+)?\\??)\\.' + key + '(\\W)'] = '\\g<1>.' + view_ids_mapping[key] + '\\g<2>'
sub_map['([bB]inding\\??(?:\\.\\w+)?\\??)\\.' + key +'\\.'+ key + '(\\W)'] = '\\g<1>.' + view_ids_mapping[key] +'.' +view_ids_mapping[key] +'\\g<2>'
sub_map['([bB]inding\\??(?:\\.\\w+)?\\??)\\.' + key + '\\.' + key + '(\\W)'] = '\\g<1>.' + view_ids_mapping[
key] + '.' + view_ids_mapping[key] + '\\g<2>'
sub_map[r'\(binding as (\w+Binding)\)\.' + key + r'(\W)'] = r'(binding as \1).' + view_ids_mapping[key] + r'\2'
sub_map['(?<=app:constraint_referenced_ids=".*?)' + i + '(?=.*?")'] = view_ids_mapping[key]
# 新增 Kotlin 特有写法适配
sub_map[r'(binding!!(?:\.\w+)*?)\.' + key + r'(\W)'] = r'\1.' + view_ids_mapping[key] + r'\2'
sub_map[r'(binding\?\?(?:\.\w+)*?)\.' + key + r'(\W)'] = r'\1.' + view_ids_mapping[key] + r'\2'
sub_map[r'(binding(?:\.\w+)*?)\.' + key + r'(\W)'] = r'\1.' + view_ids_mapping[key] + r'\2'
sub_map['this\\.' + i + '(\\W)'] = 'this.' + view_ids_mapping[key] + '\\g<1>'
sub_map['this\\.' + i + '(\\.[\\w\\?]+(?:\\([^)]*\\))?)'] = 'this.' + view_ids_mapping[key] + '\\g<1>'
# sub_map['this\\.(\\w+)(\\.[\\w\\?]+(?:\\([^)]*\\))?)'] = 'this.' + view_ids_mapping[key] + '\\g<1>\\g<2>'
sub_map['popupView\\.' + i + '(\\W)'] = 'popupView.' + view_ids_mapping[key] + '\\g<1>'
# sub_map['\\b' + i + '\\.(\\w+)(\\s*=\\s*[^\\s]+)'] = view_ids_mapping[key] + '.\\g<1>\\g<2>'
sub_map['(?<!\\()\\b' + i + '\\.(\\w+)(\\s*=\\s*[^\\s]+)'] = view_ids_mapping[key] + '.\\g<1>\\g<2>'
# sub_map['this\\.' + view_ids_mapping['title']] = 'this.title'
if i == 'imgIcon':
sub_map[r'\.into\(\s*this\.(imgIcon)\s*\)'] = r'.into(this.' + view_ids_mapping[key] + ')'
sub_map_text(sub_map)
# 改 layout 文件名
......@@ -1542,12 +1666,126 @@ def deal_res():
# 改图片内容
deal_image()
# 主执行逻辑
mapping_file = "./mapping.json"
java_path = get_path('.\\src\\main\\java')
mapping_data = load_color_mapping(mapping_file) # 调用正确的函数
# 调试信息:检查 color_ids_mapping 内容
print("Color IDs Mapping:")
for original_color, mapped_color in mapping_data.items():
print(f"{original_color}: {mapped_color}")
# 进行文件处理
process_color_files(java_path, mapping_data)
def process_color_files(src_path, color_ids):
for root, dirs, files in os.walk(src_path):
for file in files:
if file.endswith(".kt") or file.endswith(".java"):
file_path = os.path.join(root, file)
print(f"Processing file: {file_path}")
replace_color_ids_in_file(file_path, color_ids)
def replace_color_ids_in_file(file_path, color_ids_mapping):
print("replace_color_ids_in_file")
print(color_ids_mapping)
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
if "paperedto" in content:
print(f"Found 'paperedto' in: {file_path}")
if "android.R.color.paperedto" in content:
print(f"Found 'android.R.color.paperedto' in: {file_path}")
changed = False
for original_color, mapped_color in color_ids_mapping.items():
print('original_color'+original_color)
print('mapped_color'+mapped_color)
if mapped_color in content:
pattern = r'android.R.color.' + re.escape(mapped_color) + r'(?=[^\w])'
print(pattern)
replacement = 'android.R.color.' + original_color
print(replacement)
if re.search(pattern, content):
print(f"Matched: {mapped_color} → {original_color}")
matches = re.findall(pattern, content)
if matches:
print(f"Matched: {mapped_color} → {original_color} ({len(matches)} times)")
content = re.sub(pattern, replacement, content)
changed = True
if changed:
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
def get_path(path):
paths = path.split('\\')
return str(os.path.join(*paths))
def deal_global():
mapping_file_path = 'mapping.json'
src_dir = 'src/main/java'
# 加载映射文件
with open(mapping_file_path, 'r', encoding='utf-8') as f:
mapping_data = json.load(f)
class_mapping = mapping_data.get("class", {})
dir_mapping = mapping_data.get("dir", {})
# 获取混淆后的 GlobalConfig 文件名
obfuscated_name = class_mapping.get("GlobalConfig")
if not obfuscated_name:
print("❌ GlobalConfig not found in class mapping.")
return
obfuscated_filename = obfuscated_name + '.kt'
reversed_dir_mapping = {v: k for k, v in dir_mapping.items()}
found = False
for root, dirs, files in os.walk(src_dir):
if obfuscated_filename in files:
found = True
kt_file_path = os.path.join(root, obfuscated_filename)
with open(kt_file_path, 'r', encoding='utf-8') as f:
content = f.read()
original_content = content
updated = False
# 精确匹配 PACKAGE_NAME 行
match = re.search(r'(const\s+val\s+PACKAGE_NAME\s*=\s*")([^"]+)(")', content)
if match:
current_package = match.group(2)
for obf_pkg, original_pkg in reversed_dir_mapping.items():
if current_package.startswith(obf_pkg):
restored_package = current_package.replace(obf_pkg, original_pkg, 1)
new_line = f'{match.group(1)}{restored_package}{match.group(3)}'
content = content.replace(match.group(0), new_line)
print(f"✅ Updated PACKAGE_NAME in {kt_file_path}: '{current_package}' → '{restored_package}'")
updated = True
break
else:
print(f"❌ PACKAGE_NAME not found in {kt_file_path}")
if updated and content != original_content:
with open(kt_file_path, 'w', encoding='utf-8') as f:
f.write(content)
else:
print(f"ℹ️ No PACKAGE_NAME replacement needed in {kt_file_path}")
break
if not found:
print(f"❌ File {obfuscated_filename} not found under {src_dir}")
def main():
if not os.path.exists(get_path('.\\src\\main\\java\\' + applicationId[:applicationId.find('.')])):
print(get_path('.\\src\\main\\java\\' + applicationId[:applicationId.find('.')]))
......@@ -1558,6 +1796,9 @@ def main():
deal_code()
deal_global()
def load_mapping(file_path):
"""加载 mapping.json 文件并打印内容."""
try:
......@@ -1570,6 +1811,7 @@ def load_mapping(file_path):
print(f"Error loading mapping file: {e}")
return {}
def replace_identifiers_in_file(file_path, mapping, replacements_log):
"""替换单个文件中的标识符,并存储替换的内容到 JSON 日志."""
with open(file_path, 'r', encoding='utf-8') as f:
......@@ -1647,6 +1889,7 @@ def replace_identifiers_in_file(file_path, mapping, replacements_log):
else:
print("No replacements made.")
def process_files_in_directory(directory, mapping, replacements_log):
"""处理目录下的所有文件并打印文件路径."""
for root, _, files in os.walk(directory):
......@@ -1682,13 +1925,104 @@ def solve_runapplyalso():
json.dump(replacements_log, log, ensure_ascii=False, indent=4)
print(f"\nReplacement completed. Log saved to {log_file}.")
def load_color_mapping(file_path):
"""加载 mapping.json 文件并打印内容."""
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f)
print("Loaded color Mapping:")
print(json.dumps(data, indent=4, ensure_ascii=False)) # 打印映射内容
color_ids = data.get("color_ids", {})
print(f"color_ids extracted: {color_ids}") # 检查 color_ids 是否存在
return color_ids
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error loading mapping file: {e}")
return {}
# 自动下载 NLTK 的 "words" 数据包(只下载一次)
try:
nltk.data.find("corpora/words")
except LookupError:
print("🔄 正在下载 NLTK 'words' 数据集...")
nltk.download("words")
# 获取当前目录(兼容 PyInstaller 打包)
if getattr(sys, 'frozen', False):
base_dir = os.path.dirname(sys.executable)
else:
base_dir = os.path.abspath(os.path.dirname(__file__))
mapping_path = os.path.join(base_dir, "mapping.json")
word_file_path = os.path.join(base_dir, "word_file.json")
# 是否需要生成 word_file.json
need_generate = True
if os.path.exists(mapping_path):
print("✅ 已存在 mapping.json,跳过生成 word_file.json。")
need_generate = False
else:
print("✅ 不存在 mapping.json,将生成 word_file.json。")
# 删除旧的 word_file.json(若存在)
if os.path.exists(word_file_path):
try:
os.remove(word_file_path)
print("🗑️ 已删除旧的 word_file.json")
except Exception as e:
print(f"❌ 删除旧文件失败:{e}")
sys.exit(1)
# Kotlin + Java 保留关键字(统一为小写)
kotlin_java_keywords = {
kw.lower() for kw in {
'as', 'as?', 'break', 'class', 'continue', 'do', 'else', 'false', 'for',
'fun', 'if', 'in', '!in', 'interface', 'is', '!is', 'null', 'object',
'package', 'return', 'super', 'this', 'throw', 'true', 'try', 'typealias',
'typeof', 'val', 'var', 'when', 'while', 'by', 'catch', 'constructor', 'delegate',
'dynamic', 'field', 'file', 'finally', 'get', 'import', 'init', 'param',
'property', 'receiver', 'set', 'setparam', 'where', 'actual', 'abstract',
'annotation', 'companion', 'const', 'crossinline', 'data', 'enum', 'expect',
'external', 'final', 'infix', 'inline', 'inner', 'internal', 'lateinit',
'noinline', 'open', 'operator', 'out', 'override', 'private', 'protected',
'public', 'reified', 'sealed', 'suspend', 'tailrec', 'vararg', 'assert',
'boolean', 'byte', 'case', 'char', 'default', 'double', 'extends', 'float',
'goto', 'implements', 'instanceof', 'int', 'long', 'native', 'new', 'short',
'static', 'strictfp', 'switch', 'synchronized', 'throws', 'transient', 'void',
'volatile'
}
}
def generate_and_save():
sample_size = 10000
sampled = random.sample(word_list, min(sample_size, len(word_list)))
filtered = [w.lower() for w in sampled if w.lower() not in kotlin_java_keywords]
with open(word_file_path, "w", encoding="utf-8") as f:
json.dump({"word": filtered}, f, ensure_ascii=False, indent=4)
print(f"✅ 成功生成 {len(filtered)} 个小写英文单词!")
print(f"📄 文件保存至:{word_file_path}")
if __name__ == '__main__':
if need_generate:
# 加载 NLTK 单词列表并生成
try:
word_list = list(set(words.words()))
except Exception as e:
print(f"❌ 加载 NLTK 词库失败:{e}")
sys.exit(1)
generate_and_save()
# 继续执行 gradle 文件查找逻辑
if os.path.exists('build.gradle'):
gradle_path = 'build.gradle'
elif os.path.exists('build.gradle.kts'):
gradle_path = 'build.gradle.kts'
else:
exit('找不到 build.gradle 文件')
exit('❌ 找不到 build.gradle 文件')
print(f"✅ 找到构建文件:{gradle_path}")
applicationId = re.search('namespace .*?["\'](.*?)["\']', open(gradle_path, 'r', encoding='utf-8').read())[1]
print(applicationId)
main()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment