AS TestCase: XMLList

本文介绍了一个包含数字数据的表格,该表格由三行六列组成,展示了从1到18的整数序列。对于理解基本的数据结构和内容组织方式具有一定参考价值。

<mx:XMLList id="xmlContent">
<Table>
<Row>
<Cell>
<Data Type="Number">1</Data>
</Cell>
<Cell>
<Data Type="Number">2</Data>
</Cell>
<Cell>
<Data Type="Number">3</Data>
</Cell>
<Cell>
<Data Type="Number">4</Data>
</Cell>
<Cell>
<Data Type="Number">5</Data>
</Cell>
<Cell>
<Data Type="Number">6</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data Type="Number">7</Data>
</Cell>
<Cell>
<Data Type="Number">8</Data>
</Cell>
<Cell>
<Data Type="Number">9</Data>
</Cell>
<Cell>
<Data Type="Number">10</Data>
</Cell>
<Cell>
<Data Type="Number">11</Data>
</Cell>
<Cell>
<Data Type="Number">12</Data>
</Cell>
</Row>
<Row>
<Cell>
<Data Type="Number">13</Data>
</Cell>
<Cell>
<Data Type="Number">14</Data>
</Cell>
<Cell>
<Data Type="Number">15</Data>
</Cell>
<Cell>
<Data Type="Number">16</Data>
</Cell>
<Cell>
<Data Type="Number">17</Data>
</Cell>
<Cell>
<Data Type="Number">18</Data>
</Cell>
</Row>
</Table>
</mx:XMLList>

# 服务器端代码 (file_server.py) from xmlrpc.server import SimpleXMLRPCServer import os import xmlrpc.client # 用于处理Binary类型数据 import logging # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) class FileSharingServer: """文件共享服务端核心类,提供文件管理功能""" def __init__(self, shared_dir="shared_files"): """ 初始化文件共享服务 :param shared_dir: 共享目录路径,默认为当前目录下的shared_files """ # 获取共享目录的绝对路径,避免相对路径问题 self.shared_dir = os.path.abspath(shared_dir) # 创建共享目录(如果不存在) os.makedirs(self.shared_dir, exist_ok=True) logging.info(f"服务器初始化完成,共享目录: {self.shared_dir}") def _success_response(self, message, data=None): """ 生成标准成功响应格式 message: 操作结果描述 data: 携带的业务数据(默认为None) return 包含状态、编码、消息数据的字典 """ response = { "status": "success", "code": 200, "message": message } if data is not None: response["data"] = data return response def _error_response(self, message, code=500): """ 生成标准错误响应格式 message: 错误信息描述 code: HTTP风格状态码,默认500 return 包含状态、编码消息的字典 """ return { "status": "error", "code": code, "message": message } def list_files(self): """ 获取共享目录中的文件列表 return 包含文件列表的成功响应或错误响应 """ try: logging.info("处理文件列表请求") # 获取目录中所有条目(文件子目录) all_entries = os.listdir(self.shared_dir) # 过滤出文件(排除目录) files = [ f for f in all_entries if os.path.isfile(os.path.join(self.shared_dir, f)) ] logging.info(f"找到 {len(files)} 个文件") return self._success_response("文件列表获取成功", files) except PermissionError: logging.error("无权限访问共享目录") return self._error_response("无权限访问共享目录", 403) except FileNotFoundError: logging.error(f"共享目录不存在: {self.shared_dir}") return self._error_response("共享目录不存在", 404) except Exception as e: logging.exception("获取文件列表异常") return self._error_response(f"获取文件列表失败: {str(e)}") def upload_file(self, filename, file_data): """ 处理文件上传请求 filename: 目标文件名 file_data: XML-RPC Binary封装的文件数据 return 成功或错误响应 """ try: logging.info(f"上传文件请求: {filename}") # 构建文件完整路径 file_path = os.path.join(self.shared_dir, filename) # 写入文件数据 with open(file_path, "wb") as f: f.write(file_data.data) # 自动处理Binary对象 logging.info(f"文件上传成功: {filename}") return self._success_response(f"文件 '{filename}' 上传成功") except IsADirectoryError: logging.error(f"目标路径是目录: {file_path}") return self._error_response(f"目标路径是目录: {file_path}", 400) except PermissionError: logging.error(f"无权限写入文件: {file_path}") return self._error_response(f"无权限写入文件", 403) except Exception as e: logging.exception(f"文件上传异常: {filename}") return self._error_response(f"文件上传失败: {str(e)}") def download_file(self, filename): """ filename: 请求下载的文件名 return 包含文件数据的Binary对象或错误响应 """ try: logging.info(f"下载文件请求: {filename}") # 构建文件完整路径 file_path = os.path.join(self.shared_dir, filename) # 检查文件是否存在 if not os.path.exists(file_path): logging.warning(f"文件不存在: {filename}") return self._error_response(f"文件 '{filename}' 不存在", 404) # 检查是否为文件 if not os.path.isfile(file_path): return self._error_response(f"'{filename}' 不是文件", 400) # 读取文件内容并封装为Binary对象 with open(file_path, "rb") as f: binary_data = xmlrpc.client.Binary(f.read()) logging.info(f"文件下载成功: {filename}") return self._success_response("文件下载成功", binary_data) except PermissionError: logging.error(f"无权限读取文件: {file_path}") return self._error_response(f"无权限读取文件", 403) except Exception as e: logging.exception(f"文件下载异常: {filename}") return self._error_response(f"文件下载失败: {str(e)}") def delete_file(self, filename): """ 处理文件删除请求 :param filename: 要删除的文件名 :return: 成功或错误响应 """ try: logging.info(f"删除文件请求: {filename}") # 构建文件完整路径 file_path = os.path.join(self.shared_dir, filename) # 检查文件是否存在 if not os.path.exists(file_path): logging.warning(f"文件不存在: {filename}") return self._error_response(f"文件 '{filename}' 不存在", 404) # 执行删除操作 os.remove(file_path) logging.info(f"文件删除成功: {filename}") return self._success_response(f"文件 '{filename}' 删除成功") except PermissionError: logging.error(f"无权限删除文件: {file_path}") return self._error_response(f"无权限删除文件", 403) except OSError as e: logging.error(f"文件删除失败: {str(e)}") return self._error_response(f"文件删除失败: {str(e)}") except Exception as e: logging.exception(f"文件删除异常: {filename}") return self._error_response(f"文件删除失败: {str(e)}") def start_server(host="localhost", port=8000): """ 启动文件共享服务器 :param host: 监听地址,默认为localhost :param port: 监听端口,默认为8000 """ try: # 创建XML-RPC服务器实例 server = SimpleXMLRPCServer((host, port), allow_none=True) server.register_introspection_functions() # 启用内省功能 logging.info(f"服务器启动中... 地址: {host}:{port}") print(f"文件共享服务器已启动,访问地址:http://{host}:{port}") print("可用方法: list_files, upload_file, download_file, delete_file") print("按 Ctrl+C 停止服务器...") # 创建文件服务实例并注册 file_server = FileSharingServer() server.register_instance(file_server) # 启动服务循环 server.serve_forever() except KeyboardInterrupt: logging.info("服务器停止中...") print("\n服务器已停止") except Exception as e: logging.exception("服务器启动失败") print(f"服务器启动失败: {str(e)}") # 当直接运行此脚本时(非导入模块) if __name__ == "__main__": start_server() 上面是服务端代码 # 客户端代码 (file_client.py) 修改版 import xmlrpc.client import os import logging # 配置日志 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s' ) class FileSharingClient: """文件共享客户端类,提供交互式操作界面""" def __init__(self, server_url="http://localhost:8000"): """ 初始化客户端 :param server_url: 服务器地址,默认为本地8000端口 """ self.server = xmlrpc.client.ServerProxy(server_url, allow_none=True) self.server_url = server_url logging.info(f"客户端初始化完成,连接服务器: {server_url}") def list_files(self): """获取并显示服务器文件列表""" try: response = self.server.list_files() if response["status"] == "success": files = response.get("data", []) print("\n服务器文件列表:") for i, file in enumerate(files, 1): print(f"{i}. {file}") return files else: print(f"错误: {response['message']}") return [] except Exception as e: print(f"连接失败: {str(e)}") return [] def upload_file(self, local_path, remote_filename=None): """上传本地文件到服务器""" try: if not os.path.exists(local_path): print("错误: 本地文件不存在") return False remote_name = remote_filename or os.path.basename(local_path) with open(local_path, "rb") as f: file_data = xmlrpc.client.Binary(f.read()) response = self.server.upload_file(remote_name, file_data) if response["status"] == "success": print(response["message"]) return True else: print(f"错误: {response['message']}") return False except Exception as e: print(f"错误: {str(e)}") return False def download_file(self, remote_filename, local_path=None): """从服务器下载文件""" try: response = self.server.download_file(remote_filename) if response["status"] == "success": file_data = response.get("data") save_path = local_path or remote_filename with open(save_path, "wb") as f: f.write(file_data.data) print(f"文件 '{remote_filename}' 已下载为 '{save_path}'") return True else: print(f"错误: {response['message']}") return False except Exception as e: print(f"错误: {str(e)}") return False def delete_file(self, filename): """删除服务器上的文件""" try: response = self.server.delete_file(filename) if response["status"] == "success": print(response["message"]) return True else: print(f"错误: {response['message']}") return False except Exception as e: print(f"错误: {str(e)}") return False def main(): """主程序入口,提供命令行交互界面""" print("=== 文件共享客户端 ===") client = FileSharingClient() while True: print("\n操作菜单:") print("1. 列出文件") print("2. 上传文件") print("3. 下载文件") print("4. 删除文件") print("5. 退出") # 原第六个选项变为第五个 choice = input("> ").strip() if choice == "1": client.list_files() elif choice == "2": local_path = input("本地文件路径: ").strip() remote_name = input("服务器文件名 (可选): ").strip() or None client.upload_file(local_path, remote_name) elif choice == "3": remote_name = input("服务器文件名: ").strip() local_path = input("保存为 (可选): ").strip() or None client.download_file(remote_name, local_path) elif choice == "4": filename = input("要删除的文件名: ").strip() client.delete_file(filename) elif choice == "5": print("再见!") break else: print("无效输入,请重新选择!") if __name__ == "__main__": main() 上面是客户端代码,写一个文档,满足以下要求: 需提交可运行的源码电子版,以及设计报告(不少于3000字)电子版(Word版本PDF版本均需要),并内容包含需求分析、设计、测试、源码说明等内容。源码打包提交,要求在在Python3最新版环境下(Spyder或Pyzo中运行,或者命令行下用python命令执行)可运行,除Winpython已提供的库以外,不依赖于额外安装的包模块,也不依赖于特定的其他运行环境。
11-08
import queue import threading import xml.etree.ElementTree as ET import os from concurrent.futures.thread import ThreadPoolExecutor import re from pathlib import Path from collections import defaultdict import copy from pathlib import Path import openpyxl from openpyxl import Workbook import tkinter as tk from tkinter import ttk suffixes_android = ['strings.xml', 'strings_v6.xml', 'strings_v2.xml'] suffixes_ios = ['Localizable.strings', 'LocalizableAdded.strings'] title_path = set() all_differ_key_value = [] queues_name_gloable = ['remediate_list'] queues_name = {name: queue.Queue() for name in queues_name_gloable} # 创建布尔变量及选框 language_vars = {} # 公共资源 ios_self_common = [] # 定制资源 ios_self_tai = [] lock = threading.Lock() class xmlObj: def __init__(self, key, value, old_value, old_key, path, ios_key, ios_value, ios_path, languages): self.key = key self.xml_path = path self.value = value self.languages = languages self.ios_key = ios_key self.ios_value = ios_value self.ios_path = ios_path self.old_value = old_value self.old_key = old_key def __eq__(self, other): if isinstance(other, xmlObj): return (self.key == other.key and self.xml_path == other.xml_path and self.value == other.value and self.languages == other.languages and self.ios_key == other.ios_key and self.ios_value == other.ios_value and self.ios_path == other.ios_path and self.old_value == other.old_value and self.old_key == other.old_key) return False def __str__(self): attrs = [ f"Key: {self.key}", f"Value: {self.value}", f"Old Value: {self.old_value}", f"Old Key: {self.old_key}", f"XML Path: {self.xml_path}", f"iOS Key: {self.ios_key}", f"iOS Value: {self.ios_value}", f"iOS Path: {self.ios_path}", f"Languages: {self.languages}" ] return ', '.join(attrs) class iosObj: def __init__(self, key, value, path, androidKey, androidValue, androidPath, languages): self.key = key self.ios_path = path self.value = value self.androidKey = androidKey self.androidValue = androidValue self.androidPath = androidPath self.languages = languages def merge_xml_files(xml_files): try: title_path.add(xml_files.split("\\")[5]) tree = ET.parse(xml_files) root = tree.getroot() language_code = get_android_language(xml_files) android_list = [] for child in root.findall("string"): key = child.attrib.get("name") value = child.text android_list.append(xmlObj(key, value, '', '', xml_files, '', '', '', language_code)) return get_android_ios_obj(android_list) except ET.ParseError as e: print(f"Error parsing file {xml_files}: {e}") def get_android_language(xml_files): switch_dice = { "zh-rMO": "TW", "zh-rZH": "zh-rCN", "pt-rBR": "pt", "es-rLA": "es", "el-rGR": "el", "es-rES": "es", } direct_name = os.path.dirname(xml_files) # 获取目录路径 -> 'res/values-pt-rBR' # 使用正则表达式提取语言标识(如 'pt-rBR') match = re.search(r'values-([a-z]{2}(-[a-zA-Z0-9]{1,8})?)', direct_name) if match: language_code = match.group(1) # 输出: pt-rBR count = language_code.count('-') if count > 0: language_code = switch_dice.get(language_code, language_code) else: language_code = "en" return language_code def get_ios_language(file_path): direct_name = os.path.dirname(file_path) language = os.path.basename(direct_name) count = language.count('-') if count == 0: language = language.split('.')[0] elif count == 1: language = language.split('.')[0].split('-')[0] elif count == 2: language = language.split('.')[0].split('-')[2] if language == "zh": language = "zh-rCN" elif language == "Base": language = "en" elif language == "CN": language = "TW" return language def parse_strings_files(ios_files): language = get_ios_language(ios_files) pattern = re.compile(r'"([^"]+)"\s*=\s*"([^"]*)";') with open(ios_files, 'r', encoding='utf-8', errors='ignore') as f: for line in f: match = pattern.match(line) if match: key = match.group(1) value = match.group(2) key = key.replace('\\', '"') value = value.replace('\\', '"') obj = iosObj(key, value, ios_files, '', '', '', language) if "Resource" in ios_files: ios_self_common.append(obj) else: ios_self_tai.append(obj) ANDROID_FILE_PATTERN = re.compile(r'^strings(_v\d+)?\.xml$') def is_valid_android_file(file_name: str) -> bool: """判断文件是否为 Android 类型,并且不等于 menustrings.commonstrings,country_strings""" return (ANDROID_FILE_PATTERN.match(file_name) and file_name != "menustrings.xml" and file_name != "commonstrings.xml" and file_name != "country_strings.xml") def travel_root(root_path): android_list = [] path_android = [] try: for root, dirs, files in os.walk(root_path, followlinks=False): valid_android = [os.path.join(root, file) for file in files if is_valid_android_file(file) and "Build" not in os.path.abspath(os.path.join(root, file)) and file.endswith(tuple(suffixes_android)) and contains_language(os.path.join(root, file))] print(f"Directory: {root}, Android files: {valid_android}") path_android.extend(valid_android) [parse_strings_files(os.path.join(root, file)) for file in files if file.endswith(tuple(suffixes_ios))] print(f"Total Android files collected: {len(path_android)}") with ThreadPoolExecutor(max_workers=12) as executor: android_sublist = list(executor.map(merge_xml_files, path_android)) android_list.extend(android_sublist) except Exception as e: print(f"Error: {e}") return android_list def contains_language(path): for key, value in language_vars.items(): if value.get() and key in path: return True return False def get_android_ios_obj(android_list): android_self_common = [] android_self_tai = [] for items_android in android_list: if "main" in items_android.xml_path: android_self_common.append(items_android) else: android_self_tai.append(items_android) merge_xml_obj = merge_list(compare_android_ios(android_self_common, ios_self_common), compare_android_ios(android_self_tai, ios_self_tai)) return merge_xml_obj def merge_list(android_list_self_common, android_list_self_tai): with lock: merge_xml_obj = [] merge_xml_obj.extend(android_list_self_common + android_list_self_tai) return merge_xml_obj def is_same_language(android_value, ios_value): if android_value.languages == ios_value.languages: return True else: return False def get_all_differ(android_copy, ios_self): try: android_by_key_and_value = defaultdict(list) ios_by_key_and_value = defaultdict(list) flatten_list = [item for sublist in android_copy for item in sublist] for item in flatten_list: if hasattr(item, "key") and hasattr(item, "value"): key = f"{item.key}{item.value}" android_by_key_and_value[key] = item else: print(f"无效对象,不包含 key 或 value 属性: {item}") for item in ios_self: if hasattr(item, "key") and hasattr(item, "value"): key = f"{item.key}{item.value}" ios_by_key_and_value[key] = item else: print(f"无效对象,不包含 key 或 value 属性: {item}") for key in android_by_key_and_value: if key not in ios_by_key_and_value: item = android_by_key_and_value.get(key, 'default') if item != 'default': all_differ_key_value.append(item) except Exception as e: print(f"get_all_differ函数执行出错: {e}") def compare_android_ios(android_self, ios_self): print("正在比较......") get_differ_key(android_self, ios_self) android_copy = copy.deepcopy(android_self) return get_differ_value(android_copy, ios_self) def get_differ_key(android_self, ios_self): try: for self_android in android_self: for self_ios in ios_self: if self_android.value == self_ios.value and is_same_language(self_android, self_ios): if self_android.key != self_ios.key: old_key = self_android.key self_android.key = self_ios.key xml = xmlObj(self_android.key, self_android.value, self_android.value, old_key, self_android.xml_path, self_ios.key, self_ios.value, self_ios.ios_path, '') queues_name['remediate_list'].put(xml) except Exception as e: print(f"get_differ_key 报错:{e}") def get_differ_value(android_copy, ios_self): for self_android in android_copy: for self_ios in ios_self: if self_android.key == self_ios.key and is_same_language(self_android, self_ios): if self_ios.value != self_android.value: self_android.value = self_ios.value xml = xmlObj(self_android.key, self_android.value, self_android.old_value, self_android.old_key, self_android.xml_path, self_ios.key, self_ios.value, self_ios.ios_path, '') queues_name['remediate_list'].put(xml) return copy.deepcopy(android_copy) def write_dict_to_xml(data_dict): try: group_data = defaultdict(list) for obj in data_dict: for item in obj: group_data[item.xml_path].append(item) for path, items in group_data.items(): resource = ET.Element('resources') for item in items: et = ET.SubElement( resource, "string", {"name": item.key} ) et.text = item.value tree = ET.ElementTree(resource) ET.indent(tree, space=" ") # 使 XML 格式整齐 tree.write(path, encoding="utf-8", xml_declaration=True, method='xml') print(f"写入文件中{path}请稍等") except IOError as e: print(f"写入xml文件操作失败({path}): {str(e)}") except AttributeError as e: print(f"写入xml对象属性缺失: {str(e)}") except ET.ParseError as e: print(f"写入XML格式错误: {str(e)}") except Exception as e: print(f"写入xml未知错误: {str(e)}") def write_to_excel(android_list_self, is_same, path): try: print("正在写入excel") wb = Workbook() for item in android_list_self: title = get_parent_directory(item.xml_path) for elem in title_path: if elem in item.xml_path: title = elem # 根据 title 获取或创建工作表 if title in wb.sheetnames: ws = wb[title] else: ws = wb.create_sheet(title=title) if is_same: ws.append( ["old_key", "old_value", "ios_key", "ios_value", "ios_path", "android_path", "key", "value"]) else: ws.append( ["old_key", "old_value", "ios_key", "ios_value", "ios_path", "android_path", "key", "value"]) # 将数据追加到对应工作表 ws.append( [item.old_key, item.old_value, item.ios_key, item.ios_value, item.ios_path, item.xml_path, item.key, item.value]) if is_same: file_path = os.path.join(path, 'android_same.xlsx') else: file_path = os.path.join(path, 'android_not_same.xlsx') file_path = os.path.join(path, file_path) wb.save(file_path) except PermissionError as e: print(f"文件保存失败:权限不足 ({str(e)})") except FileNotFoundError as e: print(f"文件路径无效:路径不存在 ({str(e)})") except openpyxl.utils.exceptions.IllegalCharacterError as e: print(f"Excel文件数据异常:包含非法字符 ({str(e)})") except AttributeError as e: print(f"对象属性错误:请检查数据完整性 ({str(e)})") def get_parent_directory(xml_path): path = Path(xml_path) parts = path.parts for i in range(len(parts) - 1): if parts[i].lower() == 'res': return path.resolve().parts[i - 1] return None is_all_selected = False def on_submit(): selected = [lang for lang, var in language_vars.items() if var.get()] print(f"你选择了: {selected}") root.destroy() def toggle_all(): global is_all_selected is_all_selected = not is_all_selected for var in language_vars.values(): var.set(is_all_selected) # 初始化窗口 root = tk.Tk() root.title("语言选择") root.geometry("400x800") # 语言选项(使用语言代码) languages = ['ar', 'ca', 'de', 'el', 'en', 'enm', 'es-rES', 'es-rLA', 'eu', 'fr', 'pt-rBR', 'ru', 'th', 'zh-rCN', 'zh-rMO', 'zh-rZH'] # 全选按钮 ttk.Button(root, text="全选", command=toggle_all).grid(row=len(languages) + 1, column=0, padx=20, pady=20) # 提交按钮 ttk.Button(root, text="提交", command=on_submit).grid(row=len(languages) + 1, column=1, padx=20, pady=20) # 复选框按钮 for idx, lang in enumerate(languages): var = tk.BooleanVar() ttk.Checkbutton(root, text=lang, variable=var).grid(row=idx, sticky="ew", padx=10, pady=5) language_vars[lang] = var # 启动事件循环 root.mainloop() if __name__ == '__main__': android_map = {} android_root_path = input("请输入Android路径:") ios_root_path = input("请输入ios路径:") travel_root(ios_root_path) key_value_list_android = travel_root(android_root_path) ios_self_common.extend(ios_self_tai) get_all_differ(key_value_list_android, ios_self_common) data_dict = list(queues_name['remediate_list'].queue) thread_remediate_list = threading.Thread(target=write_to_excel, args=(data_dict, True, android_root_path)) thread_all_differ_key_value = threading.Thread(target=write_to_excel, args=(all_differ_key_value, False, android_root_path)) thread_remediate_list.start() thread_all_differ_key_value.start() write_dict_to_xml(key_value_list_android)
07-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值