存储过程output String[1]: Size 属性具有无效大小值0

本文解析了在执行存储过程时遇到的关于输出参数的错误,并指出了正确的使用方法,即使用三个参数版本的Parameters.Add方法。

执行存储过程出现这个错误

param[1] = comm.Parameters.Add("@Name", SqlDbType.VarChar);  //错误
param[1].Value = Name ;
param[1].Direction = ParameterDirection.Output;

output方式从存储过程传出参数 必须使用三个参数版本的,及Parameters.Add("@Name", SqlDbType.VarChar,50);

 

 

引用:

http://blog.sina.com.cn/s/blog_4cc108e80100f2l8.html

#include <iostream> #include <vector> #include <string> // 引入核心压缩解压头文件(之前的完整代码保存为 HuffmanCompress.h) #include "HuffmanCompress.h" // Windows 原生 API 头文件 #include <windows.h> #include <commdlg.h> #include <shlobj.h> #pragma comment(lib, "comdlg32.lib") #pragma comment(lib, "shell32.lib") // -------------------------- 文件/文件夹选择函数(ANSI 版本,无宽字符串)-------------------------- std::string selectSingleFile(const std::string& filter) { OPENFILENAMEA ofn; CHAR szFile[260] = { 0 }; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAMEA); ofn.hwndOwner = NULL; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = filter.c_str(); ofn.nFilterIndex = 1; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; if (GetOpenFileNameA(&ofn)) { return std::string(ofn.lpstrFile); } return ""; } std::vector<std::string> selectMultipleFiles(const std::string& filter) { std::vector<std::string> fileNames; OPENFILENAMEA ofn; CHAR szFile[1024 * 10] = { 0 }; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAMEA); ofn.hwndOwner = NULL; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = filter.c_str(); ofn.nFilterIndex = 1; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; if (GetOpenFileNameA(&ofn)) { CHAR* p = szFile; std::string folder = p; p += strlen(p) + 1; while (*p) { std::string filePath = folder + "\\" + p; fileNames.push_back(filePath); p += strlen(p) + 1; } if (fileNames.empty()) { fileNames.push_back(folder); } } return fileNames; } std::string selectDirectory(const std::string& title = "选择目录") { BROWSEINFOA bi = { 0 }; bi.lpszTitle = title.c_str(); bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI; LPITEMIDLIST pidl = SHBrowseForFolderA(&bi); if (pidl != NULL) { CHAR szPath[MAX_PATH] = { 0 }; if (SHGetPathFromIDListA(pidl, szPath)) { CoTaskMemFree(pidl); return std::string(szPath); } CoTaskMemFree(pidl); } return ""; } std::string selectSaveFile(const std::string& defaultExt, const std::string& filter) { OPENFILENAMEA ofn; CHAR szFile[260] = { 0 }; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(OPENFILENAMEA); ofn.hwndOwner = NULL; ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile); ofn.lpstrFilter = filter.c_str(); ofn.nFilterIndex = 1; ofn.lpstrDefExt = defaultExt.c_str(); ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT; if (GetSaveFileNameA(&ofn)) { return std::string(ofn.lpstrFile); } return ""; } // -------------------------- 菜单和辅助函数 -------------------------- void showMenu() { system("cls"); std::cout << "\n===== 哈夫曼压缩解压工具 =====" << std::endl; std::cout << "1. 压缩单个文件" << std::endl; std::cout << "2. 压缩多个文件(按住Ctrl多选)" << std::endl; std::cout << "3. 压缩文件夹(遍历所有文件)" << std::endl; std::cout << "4. 解压缩文件(支持.huf/.zip/.rar/.7z)" << std::endl; std::cout << "0. 退出程序" << std::endl; std::cout << "==============================" << std::endl; std::cout << "请选择操作(输入数字): "; } bool pathExists(const std::string& path) { return re::exists(path); } bool createDirRecursive(const std::string& path) { try { return re::create_directories(path); } catch (...) { return false; } } // -------------------------- 主函数(彻底修复 cin.ignore 问题)-------------------------- int main() { File_faction huffmanTool; int choice; do { showMenu(); std::cin >> choice; // 核心修复:用固定大数替代 numeric_limits,无需额外头文件 std::cin.ignore(10000, '\n'); // 忽略最多10000个字符(足够覆盖控制台输入) switch (choice) { case 1: // 压缩单个文件 { std::string inputFile = selectSingleFile("所有文件 (*.*)\0*.*\0"); if (inputFile.empty()) { std::cout << "未选择任何文件,返回菜单..." << std::endl; system("pause"); break; } std::string outputFile = selectSaveFile("huf", "哈夫曼压缩文件 (*.huf)\0*.huf\0所有文件 (*.*)\0*.*\0"); if (outputFile.empty()) { std::cout << "未选择保存路径,返回菜单..." << std::endl; system("pause"); break; } huffmanTool.setParams({ inputFile }, { outputFile }, true); huffmanTool.decide(); system("pause"); } break; case 2: // 压缩多个文件 { std::vector<std::string> inputFiles = selectMultipleFiles("所有文件 (*.*)\0*.*\0"); if (inputFiles.empty()) { std::cout << "未选择任何文件,返回菜单..." << std::endl; system("pause"); break; } std::string outputFile = selectSaveFile("huf", "哈夫曼压缩文件 (*.huf)\0*.huf\0所有文件 (*.*)\0*.*\0"); if (outputFile.empty()) { std::cout << "未选择保存路径,返回菜单..." << std::endl; system("pause"); break; } huffmanTool.setParams(inputFiles, { outputFile }, true); huffmanTool.decide(); system("pause"); } break; case 3: // 压缩文件夹 { std::string inputDir = selectDirectory("选择要压缩的文件夹"); if (inputDir.empty()) { std::cout << "未选择文件夹,返回菜单..." << std::endl; system("pause"); break; } std::string outputFile = selectSaveFile("huf", "哈夫曼压缩文件 (*.huf)\0*.huf\0所有文件 (*.*)\0*.*\0"); if (outputFile.empty()) { std::cout << "未选择保存路径,返回菜单..." << std::endl; system("pause"); break; } huffmanTool.setParams({ inputDir }, { outputFile }, true); huffmanTool.decide(); system("pause"); } break; case 4: // 解压缩文件 { std::string inputFile = selectSingleFile("压缩包文件 \0*.\0所有文件 (*.*)\0*.*\0"); if (inputFile.empty()) { std::cout << "未选择压缩包,返回菜单..." << std::endl; system("pause"); break; } std::string outputDir = selectDirectory("选择解压目标文件夹"); if (outputDir.empty()) { std::cout << "未选择解压路径,返回菜单..." << std::endl; system("pause"); break; } if (!pathExists(outputDir) && !createDirRecursive(outputDir)) { std::cout << "创建解压文件夹失败,返回菜单..." << std::endl; system("pause"); break; } huffmanTool.setParams({ inputFile }, { outputDir }, false); huffmanTool.decide(); system("pause"); } break; case 0: // 退出 std::cout << "感谢使用,再见!" << std::endl; system("pause"); break; default: // 无效选择 std::cout << "无效的选择,请重新输入!" << std::endl; system("pause"); break; } } while (choice != 0); return 0; }#pragma once #include <unordered_map> #include <filesystem> #include <iostream> #include <fstream> #include <queue> #include <vector> #include <string> #include <algorithm> #include <set> #include <cstdint> #include <system_error> #include <limits> namespace re = std::filesystem; #include <bitset> struct Node { uint8_t data; int frea; Node* left; Node* right; Node(uint8_t a, int b) : data(a), frea(b), left(nullptr), right(nullptr) {} }; struct Queue_rule { bool operator()(Node* a, Node* b) { return a->frea > b->frea; } }; class Tool { public: static void get_codes(Node* root, std::string str, std::unordered_map<uint8_t, std::string>& codes) { if (!root) return; if (!root->left && !root->right) { codes[root->data] = str.empty() ? "0" : str; return; } get_codes(root->left, str + '0', codes); get_codes(root->right, str + '1', codes); } static Node* Hafuman_Tree(std::unordered_map<uint8_t, int> rep) { std::priority_queue<Node*, std::vector<Node*>, Queue_rule> minnode; for (const auto& p : rep) minnode.push(new Node(p.first, p.second)); while (minnode.size() != 1) { Node* left = minnode.top(); minnode.pop(); Node* right = minnode.top(); minnode.pop(); Node* top = new Node('#', left->frea + right->frea); top->left = left; top->right = right; minnode.push(top); } return minnode.top(); } static std::unordered_map<uint8_t, int> getdata(std::vector<std::string> inpath) { std::unordered_map<uint8_t, int> frea; for (const auto& path : inpath) { std::ifstream in(path, std::ios::binary); if (!in) { std::cerr << "警告:文件 " << path << " 无法打开,跳过该文件" << std::endl; continue; } uint8_t bt; while (in.read(reinterpret_cast<char*>(&bt), 1)) frea[bt]++; in.close(); } return frea; } static uint64_t getFileSize(const std::string& path) { try { return re::file_size(path); } catch (const std::exception& e) { std::cerr << "警告:文件 " << path << " 大小获取失败,返回 0。错误:" << e.what() << std::endl; return 0; } } static std::vector<std::string> traverseFolder(const std::string& folderPath, const std::set<std::string>& filterExt = {}) { std::vector<std::string> filePaths; try { if (!re::exists(folderPath)) { std::cerr << "错误:" << folderPath << " 路径不存在" << std::endl; return filePaths; } if (!re::is_directory(folderPath)) { std::cerr << "错误:" << folderPath << " 不是有效文件夹" << std::endl; return filePaths; } for (const auto& entry : re::recursive_directory_iterator(folderPath)) { if (re::is_regular_file(entry.path())) { std::string filePath = entry.path().string(); if (filterExt.empty()) { filePaths.push_back(filePath); } else { std::string ext = entry.path().extension().string(); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); if (filterExt.count(ext)) { filePaths.push_back(filePath); } } } } } catch (const std::exception& e) { std::cerr << "错误:遍历文件夹 " << folderPath << " 失败。错误:" << e.what() << std::endl; } return filePaths; } static bool isCompressedFile(const std::string& filePath) { std::set<std::string> compressedExt = { ".huf", ".zip", ".rar", ".7z" }; try { std::string ext = re::path(filePath).extension().string(); std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); return compressedExt.count(ext) > 0; } catch (const std::exception& e) { std::cerr << "警告:解析文件 " << filePath << " 后缀失败。错误:" << e.what() << std::endl; return false; } } static std::string cleanFileName(const std::string& fileName) { std::string illegalChars = R"(:*?"<>|\/)"; std::string cleaned = fileName; for (char c : illegalChars) { std::replace(cleaned.begin(), cleaned.end(), c, '_'); } return cleaned; } static int getMaxCodeLength(const std::unordered_map<uint8_t, std::string>& codes) { int maxLen = 0; for (const auto& p : codes) { maxLen = std::max(maxLen, static_cast<int>(p.second.size())); } return maxLen; } }; class File_faction { private: std::vector<std::string> inpath; std::vector<std::string> outpath; bool isCompressMode; public: void setParams(const std::vector<std::string>& inPaths, const std::vector<std::string>& outPaths, bool compressMode) { inpath = inPaths; outpath = outPaths; isCompressMode = compressMode; } bool compress(std::vector<std::string> path1, std::string outpath) { if (path1.empty()) { std::cerr << "错误:压缩文件列表为空" << std::endl; return false; } auto freq = Tool::getdata(path1); if (freq.empty()) { std::cerr << "错误:所有输入文件无有效数据" << std::endl; return false; } Node* root = Tool::Hafuman_Tree(freq); std::unordered_map<uint8_t, std::string> codes; Tool::get_codes(root, "", codes); int maxCodeLen = Tool::getMaxCodeLength(codes); delete root; std::ofstream out(outpath, std::ios::binary | std::ios::trunc); if (!out) { std::cerr << "错误:无法创建输出文件 " << outpath << std::endl; return false; } int codesize = codes.size(); out.write(reinterpret_cast<char*>(&codesize), sizeof(codesize)); out.write(reinterpret_cast<char*>(&maxCodeLen), sizeof(maxCodeLen)); for (const auto& p : codes) { uint8_t bt = p.first; std::string code = p.second; int len = code.size(); out.write(reinterpret_cast<char*>(&bt), 1); out.write(reinterpret_cast<char*>(&len), sizeof(len)); std::bitset<8> bits; int bitCount = 0; for (char c : code) { bits[7 - (bitCount % 8)] = (c == '1'); bitCount++; if (bitCount % 8 == 0 || bitCount == len) { out.put(static_cast<char>(bits.to_ulong())); bits.reset(); } } } uint64_t totalSize = 0; for (const auto& path : path1) totalSize += Tool::getFileSize(path); out.write(reinterpret_cast<char*>(&totalSize), sizeof(totalSize)); for (const auto& path : path1) { re::path filePathObj(path); std::string fileName = filePathObj.filename().string(); uint64_t fileSize = Tool::getFileSize(path); int fileNameLen = fileName.size(); out.write(reinterpret_cast<char*>(&fileNameLen), sizeof(fileNameLen)); out.write(fileName.c_str(), fileNameLen); out.write(reinterpret_cast<char*>(&fileSize), sizeof(fileSize)); std::ifstream in(path, std::ios::binary); if (!in) { std::cerr << "警告:文件 " << path << " 无法打开,跳过" << std::endl; continue; } std::bitset<8> bits; int bitCount = 0; uint8_t bt; while (in.read(reinterpret_cast<char*>(&bt), 1)) { std::string code = codes[bt]; for (char c : code) { bits[7 - (bitCount % 8)] = (c == '1'); bitCount++; if (bitCount == 8) { out.put(static_cast<char>(bits.to_ulong())); bits.reset(); bitCount = 0; } } } if (bitCount > 0) { out.put(static_cast<char>(bits.to_ulong())); } in.close(); } out.close(); std::cout << "压缩完成!输出文件:" << outpath << ",总原始大小:" << totalSize << " 字节" << std::endl; return true; } bool decompress(std::vector<std::string> path1, std::vector<std::string> path2) { if (path1.empty() || path2.empty()) { std::cerr << "错误:输入路径或输出路径为空" << std::endl; return false; } std::string inPath = path1.front(); std::string outFolder = path2.front(); try { if (!re::exists(outFolder)) { std::cout << "创建解压文件夹:" << outFolder << std::endl; re::create_directories(outFolder); } // 修复核心:path对象显式转换为string std::string testFile = (re::path(outFolder) / ".test_write_perm").string(); std::ofstream testOut(testFile); if (!testOut) { std::cerr << "错误:解压文件夹 " << outFolder << " 无写入权限" << std::endl; return false; } testOut.close(); re::remove(re::path(testFile)); // 移除时也可直接用string,但显式转换更规范 } catch (const std::exception& e) { std::cerr << "错误:创建解压文件夹失败。错误:" << e.what() << std::endl; return false; } std::ifstream in(inPath, std::ios::binary); if (!in) { std::cerr << "错误:无法打开压缩包 " << inPath << std::endl; return false; } int codesize; if (!in.read(reinterpret_cast<char*>(&codesize), sizeof(codesize))) { std::cerr << "错误:压缩包损坏,无法读取编码表大小" << std::endl; in.close(); return false; } int maxCodeLen; if (!in.read(reinterpret_cast<char*>(&maxCodeLen), sizeof(maxCodeLen))) { std::cerr << "错误:压缩包损坏,无法读取最大编码长度" << std::endl; in.close(); return false; } if (maxCodeLen <= 0 || maxCodeLen > 1024) { std::cerr << "错误:无效的最大编码长度 " << maxCodeLen << ",压缩包可能损坏" << std::endl; in.close(); return false; } std::unordered_map<std::string, uint8_t> codes; for (int i = 0; i < codesize; ++i) { uint8_t bt; int len; if (!in.read(reinterpret_cast<char*>(&bt), sizeof(bt)) || !in.read(reinterpret_cast<char*>(&len), sizeof(len))) { std::cerr << "错误:编码表读取失败" << std::endl; in.close(); return false; } if (len <= 0 || len > maxCodeLen) { std::cerr << "错误:无效的编码长度 " << len << ",压缩包可能损坏" << std::endl; in.close(); return false; } std::string code; int bytes = (len + 7) / 8; for (int j = 0; j < bytes; ++j) { uint8_t b; if (!in.read(reinterpret_cast<char*>(&b), 1)) { std::cerr << "错误:压缩包损坏,编码内容读取失败" << std::endl; in.close(); return false; } std::bitset<8> bits(b); for (int k = 7; k >= 0 && code.size() < len; --k) { // 从高位(7)到低位(0)读取 code += bits[k] ? "1" : "0"; } } codes[code] = bt; } uint64_t totalSize; if (!in.read(reinterpret_cast<char*>(&totalSize), sizeof(totalSize))) { std::cerr << "错误:无法读取总文件大小" << std::endl; in.close(); return false; } int bitpos = 0; uint8_t bitbu = 0; std::string curcode; uint64_t totalDecodedSize = 0; const int MAX_STRING_LEN = std::numeric_limits<std::string::size_type>::max() / 2; while (true) { if (in.peek() == EOF) break; int fileNameLen; if (!in.read(reinterpret_cast<char*>(&fileNameLen), sizeof(fileNameLen))) { std::cerr << "错误:文件名长度读取失败" << std::endl; break; } if (fileNameLen < 0 || fileNameLen > 4096) { std::cerr << "错误:无效的文件名长度 " << fileNameLen << ",压缩包可能损坏" << std::endl; break; } std::string fileName(fileNameLen, '\0'); if (!in.read(&fileName[0], fileNameLen)) { std::cerr << "错误:文件名读取失败" << std::endl; break; } std::string cleanedFileName = Tool::cleanFileName(fileName); uint64_t fileSize; if (!in.read(reinterpret_cast<char*>(&fileSize), sizeof(fileSize))) { std::cerr << "错误:文件 " << cleanedFileName << " 大小读取失败" << std::endl; break; } if (fileSize > 1024ULL * 1024ULL * 1024ULL * 10) { std::cerr << "错误:文件 " << cleanedFileName << " 大小超出限制(" << fileSize << " 字节)" << std::endl; break; } re::path outPathObj; try { outPathObj = re::path(outFolder) / cleanedFileName; } catch (const std::exception& e) { std::cerr << "错误:拼接输出路径失败(文件名:" << cleanedFileName << ")。错误:" << e.what() << std::endl; continue; } std::string outPath = outPathObj.string(); std::ofstream out; try { out.open(outPath, std::ios::binary | std::ios::trunc); } catch (const std::exception& e) { std::cerr << "错误:创建输出文件 " << outPath << " 失败。错误:" << e.what() << std::endl; continue; } if (!out) { std::cerr << "错误:无法打开输出文件 " << outPath << "(可能无权限)" << std::endl; continue; } uint8_t writbuf[1024]; size_t bufsize = 0; uint64_t decodedSize = 0; while (decodedSize < fileSize) { if (curcode.size() >= static_cast<size_t>(maxCodeLen)) { std::cerr << "错误:文件 " << cleanedFileName << " 解压失败,编码匹配超时(可能压缩包损坏)" << std::endl; decodedSize = fileSize; out.close(); re::remove(outPathObj); // 直接用path对象删除,更高效 break; } if (curcode.size() >= MAX_STRING_LEN) { std::cerr << "错误:文件 " << cleanedFileName << " 解压失败,字符串长度超出限制" << std::endl; decodedSize = fileSize; out.close(); re::remove(outPathObj); break; } if (bitpos == 0) { if (!in.read(reinterpret_cast<char*>(&bitbu), sizeof(bitbu))) { std::cerr << "错误:压缩包损坏,文件 " << cleanedFileName << " 数据不完整" << std::endl; decodedSize = fileSize; out.close(); re::remove(outPathObj); break; } bitpos = 8; } bitpos--; bool bit = (bitbu >> bitpos) & 1; curcode += bit ? "1" : "0"; auto it = codes.find(curcode); if (it != codes.end()) { uint8_t decode = it->second; writbuf[bufsize++] = decode; decodedSize++; totalDecodedSize++; if (bufsize == 1024) { out.write(reinterpret_cast<char*>(writbuf), bufsize); bufsize = 0; } curcode.clear(); } } if (bufsize > 0 && decodedSize <= fileSize) { out.write(reinterpret_cast<char*>(writbuf), bufsize); } out.close(); if (decodedSize == fileSize) { std::cout << "文件 " << cleanedFileName << " 解压完成,大小:" << decodedSize << " 字节(预期:" << fileSize << " 字节)" << std::endl; } else { std::cerr << "文件 " << cleanedFileName << " 解压失败,已删除损坏文件" << std::endl; } } in.close(); if (totalDecodedSize == totalSize) { std::cout << "所有文件解压完成!总解码大小:" << totalDecodedSize << " 字节(与原始大小一致)" << std::endl; return true; } else { std::cerr << "警告:解压数据不完整!总解码大小:" << totalDecodedSize << " 字节(原始大小:" << totalSize << " 字节)" << std::endl; return false; } } void single_file() { if (inpath.empty() || outpath.empty()) { std::cerr << "错误:单文件处理缺少输入/输出路径" << std::endl; return; } std::string inPath = inpath.front(); std::string outPath = outpath.front(); if (isCompressMode) { std::vector<std::string> singleFile = { inPath }; re::path outPathObj(outPath); if (outPathObj.extension().empty()) { outPath += ".huf"; } compress(singleFile, outPath); } else { if (re::is_directory(outPath)) { decompress(inpath, { outPath }); } else { try { re::path outPathObj(outPath); re::create_directories(outPathObj.parent_path()); decompress(inpath, { outPathObj.parent_path().string() }); } catch (const std::exception& e) { std::cerr << "错误:创建输出文件夹失败。错误:" << e.what() << std::endl; } } } } void Multiple_Files() { if (inpath.empty() || outpath.empty()) { std::cerr << "错误:多文件处理缺少输入/输出路径" << std::endl; return; } std::string outPath = outpath.front(); if (isCompressMode) { re::path outPathObj(outPath); if (outPathObj.extension().empty()) { outPath += ".huf"; } compress(inpath, outPath); } else { std::string outFolder = outPath; try { if (!re::exists(outFolder)) { re::create_directories(outFolder); } } catch (const std::exception& e) { std::cerr << "错误:创建解压文件夹失败。错误:" << e.what() << std::endl; return; } for (const auto& zipPath : inpath) { if (Tool::isCompressedFile(zipPath)) { decompress({ zipPath }, { outFolder }); } else { std::cerr << "警告:" << zipPath << " 不是压缩包,跳过" << std::endl; } } } } void folder() { if (inpath.empty() || outpath.empty()) { std::cerr << "错误:文件夹处理缺少输入/输出路径" << std::endl; return; } std::string inFolder = inpath.front(); std::string outPath = outpath.front(); if (isCompressMode) { std::vector<std::string> allFiles = Tool::traverseFolder(inFolder); if (allFiles.empty()) { std::cerr << "错误:文件夹 " << inFolder << " 下无有效文件" << std::endl; return; } re::path outPathObj(outPath); if (outPathObj.extension().empty()) { outPath += ".huf"; } compress(allFiles, outPath); } else { if (inpath.size() == 1) { std::string zipPath = inpath.front(); if (Tool::isCompressedFile(zipPath)) { decompress(inpath, { outPath }); } else { std::cerr << "错误:文件夹解压仅支持单个压缩包输入(当前输入不是压缩包)" << std::endl; } } else { std::cerr << "错误:文件夹解压仅支持单个压缩包输入(当前输入 " << inpath.size() << " 个路径)" << std::endl; } } } void decide() { if (inpath.empty()) { std::cerr << "错误:输入路径为空" << std::endl; return; } int fileCount = 0; int folderCount = 0; int zipCount = 0; std::vector<std::string> validFiles; std::vector<std::string> validFolders; for (const auto& path : inpath) { try { if (!re::exists(path)) { std::cerr << "警告:路径 " << path << " 不存在,跳过" << std::endl; continue; } if (re::is_regular_file(path)) { fileCount++; validFiles.push_back(path); if (Tool::isCompressedFile(path)) { zipCount++; } } else if (re::is_directory(path)) { folderCount++; validFolders.push_back(path); } } catch (const std::exception& e) { std::cerr << "警告:路径 " << path << " 无效,跳过。错误:" << e.what() << std::endl; continue; } } if (folderCount > 0) { inpath = { validFolders.front() }; } else { inpath = validFiles; } if (folderCount > 0) { if (folderCount > 1) { std::cerr << "警告:仅支持单个文件夹输入,取第一个文件夹" << std::endl; } folder(); return; } if (fileCount > 1) { Multiple_Files(); return; } if (fileCount == 1) { single_file(); return; } std::cerr << "错误:无有效输入文件/文件夹" << std::endl; } };
11-26
#include <iostream> #include "custom_header.h" #include <ext/stdio_filebuf.h> #include <fstream> #include <queue> #include <unordered_map> #include <string> #include <vector> #include <bitset> #include <algorithm> #include <iomanip> #include <sstream> #include <stdexcept> #include <limits> #include <locale> #include <codecvt> #include <windows.h> #include <cstdio> // 兼容中文/空格路径的文件打开函数(MinGW/GCC 专用) template <typename StreamType> StreamType openFile(const std::string& utf8Path, std::ios::openmode mode) { StreamType stream; #ifdef _WIN32 try { // 将UTF-8路径转为宽字符 std::wstring_convert<std::codecvt_utf8<wchar_t>> converter; std::wstring wpath = converter.from_bytes(utf8Path); // 转换C++流的openmode为C的文件打开模式 const wchar_t* cMode = nullptr; if (mode & std::ios::binary) { if (mode & std::ios::out) { cMode = (mode & std::ios::app) ? L"ab" : L"wb"; } else { cMode = L"rb"; } } else { if (mode & std::ios::out) { cMode = (mode & std::ios::app) ? L"a" : L"w"; } else { cMode = L"r"; } } // 使用Windows API打开宽路径文件 FILE* fp = nullptr; if (_wfopen_s(&fp, wpath.c_str(), cMode) == 0) { // 使用 GNU 扩展的 stdio_filebuf 绑定 FILE* using FileBuf = __gnu_cxx::stdio_filebuf<char>; stream.rdbuf(new FileBuf(fp, mode)); } else { // 打开失败,尝试用窄路径 stream.open(utf8Path, mode); } } catch (const std::range_error&) { // 转换失败,直接用窄路径 stream.open(utf8Path, mode); } #else stream.open(utf8Path, mode); #endif return stream; } // 新增:将 GBK 编码的字符串转换为 UTF-8 编码的字符串 // 用于解决 VS Code 终端中文输入的编码问题 std::string gbk_to_utf8(const std::string& gbk_str) { int len = MultiByteToWideChar(CP_ACP, 0, gbk_str.c_str(), -1, nullptr, 0); std::wstring wstr(len, L'\0'); MultiByteToWideChar(CP_ACP, 0, gbk_str.c_str(), -1, &wstr[0], len); len = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, nullptr, 0, nullptr, nullptr); std::string utf8_str(len, '\0'); WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &utf8_str[0], len, nullptr, nullptr); return utf8_str; } // 定义大文件分块大小(8MB) const size_t CHUNK_SIZE = 8 * 1024 * 1024; // Huffman树节点结构 struct HuffmanNode { char data; unsigned long long freq; HuffmanNode* left, * right; HuffmanNode(char data, unsigned long long freq) : data(data), freq(freq), left(nullptr), right(nullptr) {} }; // 用于优先队列的比较函数,构建最小堆(频率小的节点优先级高) struct Compare { bool operator()(HuffmanNode* l, HuffmanNode* r) { return (l->freq > r->freq); } }; // 频率统计函数,支持进度显示 void calculateFrequency(const std::string& inputFile, std::unordered_map<char, unsigned long long>& freqMap) { std::ifstream file = openFile<std::ifstream>(inputFile, std::ios::binary | std::ios::ate); if (!file.is_open()) { throw std::runtime_error("无法打开输入文件: " + inputFile); } uint64_t fileSize = file.tellg(); file.seekg(0, std::ios::beg); if (fileSize == 0) { file.close(); return; } if (fileSize > 1024ULL * 1024 * 1024 * 10) { file.close(); throw std::runtime_error("文件过大,超过10GB限制"); } char* buffer = new char[CHUNK_SIZE]; uint64_t bytesRead = 0; uint64_t lastProgress = 0; try { while (file.read(buffer, CHUNK_SIZE) || file.gcount() > 0) { std::streamsize bytes = file.gcount(); if (bytes <= 0) break; for (std::streamsize i = 0; i < bytes; ++i) { freqMap[buffer[i]]++; } bytesRead += bytes; uint64_t progress = (bytesRead * 100) / fileSize; if (progress != lastProgress) { lastProgress = progress; std::cout << "\r正在统计频率: " << progress << "% " << std::flush; } } std::cout << "\r正在统计频率: 100% " << std::endl; } catch (...) { delete[] buffer; file.close(); throw; } delete[] buffer; file.close(); } // 构建Huffman树并返回根节点 HuffmanNode* buildHuffmanTree(const std::unordered_map<char, unsigned long long>& freqMap) { std::priority_queue<HuffmanNode*, std::vector<HuffmanNode*>, Compare> minHeap; for (const auto& pair : freqMap) { minHeap.push(new HuffmanNode(pair.first, pair.second)); } if (minHeap.empty()) { return nullptr; } while (minHeap.size() != 1) { HuffmanNode* left = minHeap.top(); minHeap.pop(); HuffmanNode* right = minHeap.top(); minHeap.pop(); HuffmanNode* top = new HuffmanNode('$', left->freq + right->freq); top->left = left; top->right = right; minHeap.push(top); } return minHeap.top(); } // 生成Huffman编码表(递归函数) void generateCodes(HuffmanNode* root, const std::string& str, std::unordered_map<char, std::string>& huffmanCode) { if (root == nullptr) { return; } if (!root->left && !root->right) { huffmanCode[root->data] = str.empty() ? "0" : str; } generateCodes(root->left, str + "0", huffmanCode); generateCodes(root->right, str + "1", huffmanCode); } // 序列化Huffman树 void serializeTree(HuffmanNode* root, std::ofstream& outFile) { if (root == nullptr) { char marker = '#'; outFile.write(&marker, sizeof(marker)); return; } bool isLeaf = (!root->left && !root->right); outFile.write(reinterpret_cast<const char*>(&isLeaf), sizeof(isLeaf)); if (isLeaf) { outFile.write(&root->data, sizeof(root->data)); } serializeTree(root->left, outFile); serializeTree(root->right, outFile); } // 反序列化Huffman树 HuffmanNode* deserializeTree(std::ifstream& inFile) { char marker; if (!inFile.get(marker)) { return nullptr; } if (marker == '#') { return nullptr; } bool isLeaf = (marker != 0); if (isLeaf) { char data; if (!inFile.read(&data, sizeof(data))) { throw std::runtime_error("文件损坏:无法读取叶子节点数据。"); } return new HuffmanNode(data, 0); } else { HuffmanNode* leftChild = deserializeTree(inFile); HuffmanNode* rightChild = deserializeTree(inFile); HuffmanNode* root = new HuffmanNode('$', 0); root->left = leftChild; root->right = rightChild; return root; } } // 辅助函数:释放Huffman树占用的内存 void deleteHuffmanTree(HuffmanNode* root) { if (root == nullptr) return; deleteHuffmanTree(root->left); deleteHuffmanTree(root->right); delete root; } // 文件压缩函数 void compressFile(const std::string& inputFile, const std::string& outputFile) { try { std::cout << "正在压缩文件 " << inputFile << "..." << std::endl; std::unordered_map<char, unsigned long long> freqMap; calculateFrequency(inputFile, freqMap); if (freqMap.empty()) { std::ofstream outFile = openFile<std::ofstream>(outputFile, std::ios::binary); const char* magic = "HUFF"; outFile.write(magic, 4); serializeTree(nullptr, outFile); uint64_t originalSize = 0; outFile.write(reinterpret_cast<const char*>(&originalSize), sizeof(originalSize)); uint8_t padding = 0; outFile.write(reinterpret_cast<const char*>(&padding), sizeof(padding)); outFile.close(); std::cout << "压缩完成(空文件),输出为 " << outputFile << std::endl; return; } HuffmanNode* root = buildHuffmanTree(freqMap); std::unordered_map<char, std::string> huffmanCode; generateCodes(root, "", huffmanCode); std::ifstream inFile = openFile<std::ifstream>(inputFile, std::ios::binary); if (!inFile.is_open()) { throw std::runtime_error("无法打开输入文件: " + inputFile); } std::ofstream outFile = openFile<std::ofstream>(outputFile, std::ios::binary); if (!outFile.is_open()) { deleteHuffmanTree(root); throw std::runtime_error("无法打开输出文件: " + outputFile); } const char* magic = "HUFF"; outFile.write(magic, 4); serializeTree(root, outFile); inFile.seekg(0, std::ios::end); uint64_t originalFileSize = inFile.tellg(); inFile.seekg(0, std::ios::beg); outFile.write(reinterpret_cast<const char*>(&originalFileSize), sizeof(originalFileSize)); char buffer[CHUNK_SIZE]; std::string bitString; uint64_t bytesReadTotal = 0; uint64_t lastProgress = 0; while (inFile.read(buffer, CHUNK_SIZE) || inFile.gcount() > 0) { std::streamsize bytesRead = inFile.gcount(); if (bytesRead <= 0) break; bytesReadTotal += bytesRead; for (std::streamsize i = 0; i < bytesRead; ++i) { bitString += huffmanCode[buffer[i]]; while (bitString.size() >= 8) { std::string byteStr = bitString.substr(0, 8); bitString = bitString.substr(8); char byte = static_cast<char>(std::bitset<8>(byteStr).to_ulong()); outFile.put(byte); } } uint64_t progress = (bytesReadTotal * 100) / originalFileSize; if (progress != lastProgress) { lastProgress = progress; std::cout << "\r正在压缩: " << progress << "% " << std::flush; } } uint8_t padding = 0; if (!bitString.empty()) { padding = 8 - bitString.size(); bitString += std::string(padding, '0'); char byte = static_cast<char>(std::bitset<8>(bitString).to_ulong()); outFile.put(byte); } outFile.put(static_cast<char>(padding)); std::cout << "\r正在压缩: 100% " << std::endl; inFile.close(); outFile.close(); deleteHuffmanTree(root); std::ifstream compressedFile = openFile<std::ifstream>(outputFile, std::ios::binary | std::ios::ate); uint64_t compressedFileSize = compressedFile.tellg(); compressedFile.close(); double compressionRatio = (1.0 - static_cast<double>(compressedFileSize) / originalFileSize) * 100.0; std::cout << std::fixed << std::setprecision(2); std::cout << "原始大小:" << originalFileSize << "字节" << std::endl; std::cout << "压缩后大小:" << compressedFileSize << "字节" << std::endl; std::cout << "压缩比:" << compressionRatio << "%" << std::endl; std::cout << "文件压缩完成,输出为 " << outputFile << std::endl; } catch (const std::exception& e) { std::cerr << "压缩失败: " << e.what() << std::endl; exit(1); } } // 文件解压函数 void decompressFile(const std::string& inputFile, const std::string& outputFile) { std::ifstream inFile; std::ofstream outFile; HuffmanNode* root = nullptr; try { std::cout << "正在解压文件 " << inputFile << "..." << std::endl; inFile = openFile<std::ifstream>(inputFile, std::ios::binary | std::ios::ate); if (!inFile.is_open()) { throw std::runtime_error("无法打开文件 " + inputFile); } uint64_t fileSize = inFile.tellg(); inFile.seekg(0, std::ios::beg); if (fileSize == 0) { inFile.close(); outFile = openFile<std::ofstream>(outputFile, std::ios::binary); if (outFile.is_open()) outFile.close(); std::cout << "解压完成(空文件),输出为 " << outputFile << std::endl; return; } char magic[4]; inFile.read(magic, 4); if (std::string(magic, 4) != "HUFF") { inFile.close(); throw std::runtime_error("无效的Huffman压缩文件"); } root = deserializeTree(inFile); if (!root) { inFile.close(); outFile = openFile<std::ofstream>(outputFile, std::ios::binary); if (outFile.is_open()) outFile.close(); std::cout << "解压完成(空文件或树结构损坏),输出为 " << outputFile << std::endl; return; } uint64_t originalFileSize; if (!inFile.read(reinterpret_cast<char*>(&originalFileSize), sizeof(originalFileSize))) { throw std::runtime_error("文件损坏:无法读取原始文件大小"); } outFile = openFile<std::ofstream>(outputFile, std::ios::binary); if (!outFile.is_open()) { throw std::runtime_error("无法打开输出文件 " + outputFile); } std::streampos dataStart = inFile.tellg(); uint64_t dataSize = fileSize - dataStart - 1; HuffmanNode* currentNode = root; char byte; uint64_t bytesWritten = 0; uint64_t bytesProcessed = 0; uint64_t lastProgress = 0; inFile.seekg(-1, std::ios::end); uint8_t padding; if (!inFile.read(reinterpret_cast<char*>(&padding), 1)) { throw std::runtime_error("文件损坏:无法读取填充位数"); } inFile.seekg(dataStart); while (bytesProcessed < dataSize && bytesWritten < originalFileSize) { if (!inFile.get(byte)) { bytesProcessed = dataSize; break; } bytesProcessed++; bool isLastByte = (bytesProcessed == dataSize); for (int i = 7; i >= 0; --i) { if (isLastByte && i < padding) { continue; } bool bit = (byte >> i) & 1; currentNode = bit ? currentNode->right : currentNode->left; if (currentNode == nullptr) { throw std::runtime_error("文件损坏或Huffman树结构错误"); } if (!currentNode->left && !currentNode->right) { outFile.put(currentNode->data); bytesWritten++; if (bytesWritten == originalFileSize) { goto end_decoding; } currentNode = root; } } if (dataSize > 0) { uint64_t progress = (bytesProcessed * 100) / dataSize; if (progress != lastProgress) { lastProgress = progress; std::cout << "\r正在解压: " << progress << "% " << std::flush; } } } std::cout << "\r正在解压: 100% " << std::endl; end_decoding: outFile.close(); inFile.close(); deleteHuffmanTree(root); if (bytesWritten == originalFileSize) { std::cout << "文件解压完成,输出为 " << outputFile << std::endl; } else { std::cerr << "警告:解压可能不完整。写入 " << bytesWritten << " 字节,预期 " << originalFileSize << " 字节。文件可能已损坏。" << std::endl; } } catch (const std::exception& e) { if (outFile.is_open()) outFile.close(); if (inFile.is_open()) inFile.close(); deleteHuffmanTree(root); std::cerr << "解压失败: " << e.what() << std::endl; exit(1); } } // 显示程序使用帮助 void showHelp() { std::cout << "Huffman 压缩解压工具使用方法:" << std::endl; std::cout << " 压缩文件: huffman -c 输入文件 输出文件" << std::endl; std::cout << " 解压文件: huffman -d 输入文件 输出文件" << std::endl; std::cout << " 显示帮助: huffman -h" << std::endl; } // 主函数 int main(int argc, char* argv[]) { std::ios_base::sync_with_stdio(false); std::cin.tie(NULL); // 命令行模式 if (argc >= 2) { std::string option = argv[1]; if (option == "-h") { showHelp(); return 0; } else if (option == "-c" && argc == 4) { // 对命令行传入的中文路径进行编码转换 std::string input = gbk_to_utf8(argv[2]); std::string output = gbk_to_utf8(argv[3]); compressFile(input, output); return 0; } else if (option == "-d" && argc == 4) { // 对命令行传入的中文路径进行编码转换 std::string input = gbk_to_utf8(argv[2]); std::string output = gbk_to_utf8(argv[3]); decompressFile(input, output); return 0; } else { std::cerr << "无效的命令参数" << std::endl; showHelp(); return 1; } } // 交互模式 while (true) { std::cout << "\n==== Huffman 压缩解压工具 ====" << std::endl; std::cout << "1. 压缩文件" << std::endl; std::cout << "2. 解压文件" << std::endl; std::cout << "3. 显示帮助" << std::endl; std::cout << "4. 退出程序" << std::endl; std::cout << "请选择操作(1/2/3/4): " << std::endl; int choice; std::cin >> choice; std::cin.ignore((std::numeric_limits<std::streamsize>::max)(), '\n'); std::string inputFile, outputFile; switch (choice) { case 1: std::cout << "请输入要压缩的文件名:" << std::endl; std::getline(std::cin, inputFile); std::cout << "请输入压缩后的输出文件名:" << std::endl; std::getline(std::cin, outputFile); // 对交互模式输入的中文路径进行编码转换 compressFile(gbk_to_utf8(inputFile), gbk_to_utf8(outputFile)); std::cout << "\n操作完成,返回主菜单。" << std::endl; break; case 2: std::cout << "请输入要解压的文件名:" << std::endl; std::getline(std::cin, inputFile); std::cout << "请输入解压后的输出文件名:" << std::endl; std::getline(std::cin, outputFile); // 对交互模式输入的中文路径进行编码转换 decompressFile(gbk_to_utf8(inputFile), gbk_to_utf8(outputFile)); std::cout << "\n操作完成,返回主菜单。" << std::endl; break; case 3: showHelp(); break; case 4: std::cout << "感谢使用,程序即将退出。" << std::endl; return 0; default: std::cout << "无效的选择,请重新输入。" << std::endl; break; } } return 0; } 检测到 #include 错误。请更新 includePath。已为此翻译单元(D:\MyProjects\huffman1.cpp)禁用波形曲线。
11-27
#include #include #include #include #include #include #include #include #include #include #include #include <windows.h> using namespace std; // 解析逗号分隔的浮点数 std::vector parse_doubles(const std::string& data) { std::vector values; std::istringstream iss(data); std::string token; while (std::getline(iss, token, ',')) { if (!token.empty()) { try { values.push_back(std::stod(token)); } catch (const std::exception& e) { std::cerr << "解析错误: " << e.what() << " - 无效: " << token << std::endl; } } } return values; } // 运动状态枚举 enum class MoveType { UNKNOWN, RAPID_MOVE, // G0 FEED_MOVE, // G1 ARC_MOVE // G2/G3 }; // 主转换函数 void convert_to_gcode(const std::string& input_file, const std::string& output_file) { std::ifstream in(input_file); if (!in.is_open()) { std::cerr << "无法打开输入文件: " << input_file << std::endl; return; } std::ofstream out(output_file); if (!out.is_open()) { std::cerr << "无法打开输出文件: " << output_file << std::endl; return; } // 状态变量 enum class MoveMode { RAPID, FEED }; MoveMode current_mode = MoveMode::RAPID; bool compensation_on = false; bool first_position = true; int sequence = 1; // 新增:下一个移动是否为快速移动的标志 bool next_move_is_rapid = false; // 当前坐标 double current_x = 0.0; double current_y = 0.0; double current_z = 0.0; // 圆弧信息结构 struct ArcInfo { double center_x, center_y, center_z; double i, j, k; double r, t, constant, d, c; }; std::optional<ArcInfo> pending_arc; // 新增:存储最近的运动状态(用于回溯) std::deque<MoveType> move_history; // 初始化OPENFILENAMEW结构(使用宽字符版本) OPENFILENAMEW ofn; wchar_t szFile[260] = { 0 }; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile) / sizeof(*szFile); ofn.lpstrFilter = L"所有文件\0*.*\0文本文件\0*.TXT\0"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; string str1; // 显示打开文件对话框 if (GetOpenFileNameW(&ofn)) { wchar_t* wText = szFile; DWORD dwNum = WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, NULL, 0, NULL, FALSE); char* psText; psText = new char[dwNum]; WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, psText, dwNum, NULL, FALSE); str1 = psText; delete[]psText; } else { // 用户取消选择 std::cerr << "未选择文件" << std::endl; return; } // 1. 打开文件 std::ifstream file1(str1); if (!file1.is_open()) { std::cerr << "错误:无法打开文件" << std::endl; } // 2. 逐行读取文件 std::string line1; int line_number1 = 0; while (std::getline(file1, line1)) { if (line1.find("31: oM[1]") != std::string::npos) { break; } else { out << line1 << std::endl; } } sequence = 1; std::string line; int line_num = 0; int IRAPID = 0, IFEDRAT = 0; while (std::getline(in, line)) { line_num++; // 移除行尾的回车符 if (!line.empty() && line.back() == '\r') { line.pop_back(); } // 忽略空行 if (line.empty()) continue; // 处理不同命令 if (line.starts_with("PAINT/")) { continue; } else if (line.starts_with("RAPID")) { current_mode = MoveMode::RAPID; IRAPID++; next_move_is_rapid = true; } else if (line.starts_with("FEDRAT")) { current_mode = MoveMode::FEED; IFEDRAT++; next_move_is_rapid = false; } else if (line.starts_with("CUTCOM/LEFT,1")) { compensation_on = true; } else if (line.starts_with("CUTCOM/OFF")) { compensation_on = false; } else if (line.starts_with("GOTO/")) { std::string data = line.substr(5); auto values = parse_doubles(data); if (values.size() < 3) { std::cerr << "行 " << line_num << ": GOTO需要至少3个" << std::endl; continue; } double x = values[0]; double y = values[1]; double z = values[2]; // 处理圆弧终点 if (pending_arc) { auto& arc = *pending_arc; pending_arc.reset(); // 确定圆弧方向 std::string arc_dir = (arc.i * arc.j * arc.k < 0) ? "o[+]" : "o[-]"; std::string g_code = (arc.i * arc.j * arc.k < 0) ? "G03" : "G02"; // 计算圆心相对于起点的偏移 double i_offset = arc.center_x; double j_offset = arc.center_y; double k_offset = arc.center_z; // 输出圆弧移动 out << "31: oA[" << sequence << "]( " << arc_dir << " x[/" << x << "/" << arc.center_x << "] " << "y[/" << y << "/" << arc.center_y << "] " << "z[/" << z << "/" << arc.center_z << "] )\n"; out << "32: " << g_code << " X" << x << " Y" << y << " Z" << z << " I" << i_offset << " J" << j_offset << " K" << k_offset << "\n"; sequence++; // 圆弧移动后重置快速移动标志 next_move_is_rapid = false; // 记录圆弧移动状态 move_history.push_back(MoveType::ARC_MOVE); if (move_history.size() > 10) { // 限制历史记录大小 move_history.pop_front(); } } else { // 正常GOTO处理 if (next_move_is_rapid) { // 快速移动处理 if (IRAPID == 1) { // 第一次快速移动的特殊处理 out << "31: oM[" << sequence << "]( x[/" << x << "] y[/" << y << "] )\n"; out << "10: nctx(BFP)" << "\n"; out << "10: nctx(0)" << "\n"; out << "10: arg(I : firstPosition : = 1)" << "\n"; out << "32: G0 X" << x << " Y" << y << "\n"; sequence++; out << "31: oM[" << sequence << "]( z[/" << z << "] )\n"; out << "32: G0 Z" << z << "\n"; sequence++; out << "10: arg(I : firstPosition : = 0)" << "\n"; out << "10: proc(firstPosition3D)" << "\n"; out << "10: nctx(AFP)" << "\n"; out << "10: nctx(0)" << "\n"; // 记录两个快速移动 move_history.push_back(MoveType::RAPID_MOVE); move_history.push_back(MoveType::RAPID_MOVE); } else { // 非第一次快速移动:检查上一个运动状态 if (!move_history.empty()) { MoveType last_move = move_history.back(); if (last_move == MoveType::FEED_MOVE) { out << "进给\n"; // 输出上一个运动状态 } } out << "31: oM[" << sequence << "]( x[/" << x << "] y[/" << y << "] z[/" << z << "] )\n"; out << "32: G0 X" << x << " Y" << y << " Z" << z << "\n"; sequence++; // 记录当前移动状态 move_history.push_back(MoveType::RAPID_MOVE); } // 限制历史记录大小 if (move_history.size() > 10) { move_history.pop_front(); } // 重置快速移动标志 next_move_is_rapid = false; } else { // 进给移动处理 out << "31: oL[" << sequence << "]( x[/" << x << "] y[/" << y << "] z[/" << z << "] )\n"; out << "32: G1 X" << x << " Y" << y << " Z" << z << "\n"; sequence++; // 记录当前移动状态 move_history.push_back(MoveType::FEED_MOVE); if (move_history.size() > 10) { move_history.pop_front(); } } } // 更新当前位置 current_x = x; current_y = y; current_z = z; } else if (line.starts_with("CIRCLE/")) { std::string data = line.substr(7); auto values = parse_doubles(data); if (values.size() < 11) { std::cerr << "行 " << line_num << ": CIRCLE需要11" << std::endl; continue; } // 存储圆弧信息用于下一行处理 pending_arc = ArcInfo{ values[0], values[1], values[2], // X, Y, Z 圆弧中心 values[3], values[4], values[5], // i, j, k 刀轴矢量 values[6], values[7], values[8], // R, T, constant values[9], values[10] // D, C }; } } std::string line3, target = "12: clr part 1"; bool found = false; // 逐行读取文件 while (std::getline(file1, line3)) { // 如果已经找到目标,则直接输出后续每一行 if (found) { out << line3 << "\n"; } // 如果还没找到,则检查当前行是否包含目标字符串 else if (line3.find(target) != std::string::npos) { found = true; out << line3 << "\n"; } } } int main() { // 初始化OPENFILENAMEW结构 OPENFILENAMEW ofn; wchar_t szFile[260] = { 0 }; ZeroMemory(&ofn, sizeof(ofn)); ofn.lStructSize = sizeof(ofn); ofn.lpstrFile = szFile; ofn.nMaxFile = sizeof(szFile) / sizeof(szFile); ofn.lpstrFilter = L"所有文件\0.\0文本文件\0.TXT\0"; ofn.nFilterIndex = 1; ofn.lpstrFileTitle = NULL; ofn.nMaxFileTitle = 0; ofn.lpstrInitialDir = NULL; ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; string str; // 显示打开文件对话框 if (GetOpenFileNameW(&ofn)) { wchar_t* wText = szFile; DWORD dwNum = WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, NULL, 0, NULL, FALSE); char* psText; psText = new char[dwNum]; WideCharToMultiByte(CP_OEMCP, NULL, wText, -1, psText, dwNum, NULL, FALSE); str = psText; delete[]psText; } else { std::cerr << "未选择文件" << std::endl; return 1; } std::string input_file = str; std::string output_file = "E:\\h.txt"; convert_to_gcode(input_file, output_file); return 0; } 现在我需要继续对代码进行修改: 我需要对刀具补偿的字符串判断添加一个右侧补偿的字符串"CUTCOM/RIGHT,1"。如果搜索到字符串"CUTCOM/LEFT,1"或者"CUTCOM/RIGHT,1"。则在第每次进给移动G1之前判断前一个移动类型是否是快速移动G0,如果搜索到字符串"CUTCOM/LEFT,1"或者"CUTCOM/RIGHT,1"并且进给移动G1之前为快速移动G0,则在进给移动G1之后输出字符串”刀补“。 我需要判断是否搜索到字符串"CUTCOM/OFF",如果搜索到字符串"CUTCOM/OFF",则判断快速移动G0之前是否是进给移动G1,如果是,则在搜索到的G1运动之前一行输出字符串”刀补关“,如下: ”刀补关“ G1 X10 Y0 G0 X10 Y0 Z50
最新发布
11-29
#!/usr/bin/env python # -*- coding: utf-8 -*- import os import re import sys import argparse import xlwt from collections import defaultdict # 分区名称映射表(前缀 → 友好名称) PARTITION_NAME_MAP = { '02_': 'system', '03_': 'vendor', '04_': 'product', '05_': 'odm', '06_': 'my_product', '07_': 'my_engineering', '08_': 'my_stock', '09_': 'my_heytap', '10_': 'my_company', '11_': 'my_carrier', '12_': 'my_region', '13_': 'my_preload', '14_': 'data', '15_': 'my_bigball', '16_': 'my_manifest', '17_system_dlkm': 'system_dlkm', # 新增独立分区 '17_vendor_dlkm': 'vendor_dlkm', # 新增独立分区 '17_cache': 'cache' # 新增独立分区 } def parse_du_file(file_path): """解析du命令输出文件并转换为MB""" data = {} try: with open(file_path, 'r') as f: for line in f: if 'Permission denied' in line or 'No such file' in line or not line.strip(): continue match = re.match(r'(\d+\.?\d*)\s*([KMG]?)[Bb]?\s+(.*)', line.strip()) if match: size, unit, path = match.groups() size = float(size) # 单位转换到MB if unit == 'K': size = size / 1024.0 elif unit == '': size = size / (1024*1024.0) # 默认字节转MB elif unit == 'M': pass # 已经是MB elif unit == 'G': size = size * 1024.0 data[path] = round(size, 4) # 保留两位小数 except IOError as e: print("警告: 无法读取文件 {}: {}".format(file_path, str(e))) return data def extract_file_prefix(filename): """提取文件前缀""" if filename.startswith('17_'): # 移除.txt扩展名后返回完整标识 return filename.replace('.txt', '') match = re.match(r'^(\d+_)', filename) return match.group(1) if match else "other_" def is_main_partition_file(filename, prefix): """检查是否为主分区文件""" # 17_开头的文件都视为主分区文件 if prefix.startswith('17_'): return True # 使用兼容的字符串拼接方式替代f-string expected_name = prefix + PARTITION_NAME_MAP[prefix] + ".txt" return filename == expected_name def generate_dual_report(folder1, folder2, output_xlsx): """生成双机对比报告""" # 获取文件夹名称用于显示 folder1_name = os.path.basename(os.path.normpath(folder1)) folder2_name = os.path.basename(os.path.normpath(folder2)) # 验证文件夹是否存在 for folder in [folder1, folder2]: if not os.path.exists(folder): print("错误: 目录不存在 - {}".format(folder)) return "目录 {} 不存在,请检查路径".format(folder) if not os.path.isdir(folder): print("错误: 路径不是目录 - {}".format(folder)) return "{} 不是有效目录".format(folder) # 初始化数据结构 # 主文件数据(用于总览页) machine1_main_data = {} # {prefix: {path: size}} machine2_main_data = {} # {prefix: {path: size}} # 所有文件数据(用于分区详细页) machine1_all_files = defaultdict(dict) # {filename: {path: size}} machine2_all_files = defaultdict(dict) # {filename: {path: size}} # 收集数据 for folder_path, main_dict, all_dict in [ (folder1, machine1_main_data, machine1_all_files), (folder2, machine2_main_data, machine2_all_files) ]: print("处理目录: {}".format(folder_path)) try: for filename in os.listdir(folder_path): if not filename.endswith('.txt'): continue # 提取文件前缀 prefix = extract_file_prefix(filename) # 跳过01_mount.txt和无效前缀 if prefix == '01_' or prefix not in PARTITION_NAME_MAP: continue file_path = os.path.join(folder_path, filename) partition_name = PARTITION_NAME_MAP[prefix] # 解析文件数据 file_data = parse_du_file(file_path) # 添加到所有文件数据 all_dict[filename] = file_data # 如果是主文件,添加到主文件数据(用于总览页) if is_main_partition_file(filename, prefix): print("解析主分区文件: {}".format(file_path)) main_dict[prefix] = file_data except OSError as e: print("目录访问错误: {}".format(str(e))) return "无法访问目录 {}: {}".format(folder_path, str(e)) # 创建Excel工作簿 try: wb = xlwt.Workbook(encoding='utf-8') header_style = xlwt.easyxf('font: bold on') title_style = xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre') normal_style = xlwt.easyxf() added_style = xlwt.easyxf('pattern: pattern solid, fore_colour light_green;') removed_style = xlwt.easyxf('pattern: pattern solid, fore_colour rose;') summary_style = xlwt.easyxf('font: bold on, color blue;') # ====== 创建总览Sheet页(添加备注列) ====== ws_overview = wb.add_sheet('总览') print("创建总览Sheet页(仅主文件数据)") current_row = 0 # 写入总览标题(扩展列数) ws_overview.write_merge( current_row, current_row, 0, 5, # 扩展到6列 "存储使用总览(仅主分区文件)", title_style ) current_row += 1 # 写入文件夹名称 ws_overview.write(current_row, 1, folder1_name, header_style) ws_overview.write(current_row, 2, folder2_name, header_style) current_row += 1 # 写入表头(增加备注列) headers = ['分区', '总大小(MB)', '总大小(MB)', '差(MB)', '标记', '增大TOP路径'] for col, header in enumerate(headers): ws_overview.write(current_row, col, header, header_style) current_row += 1 # 存储各分区汇总数据 overview_data = [] total_machine1 = 0.0 total_machine2 = 0.0 # 按分区顺序处理数据 for prefix in sorted(PARTITION_NAME_MAP.keys()): partition_name = PARTITION_NAME_MAP[prefix] # 跳过data分区 if partition_name == 'data': continue # 获取主文件数据 data1 = machine1_main_data.get(prefix, {}) data2 = machine2_main_data.get(prefix, {}) # 计算主文件总大小 partition_total1 = round(sum(data1.values()), 2) partition_total2 = round(sum(data2.values()), 2) diff = partition_total1 - partition_total2 # 更新总计 total_machine1 += partition_total1 total_machine2 += partition_total2 # 确定标记样式 if diff > 0: mark = "增加" style = added_style elif diff < 0: mark = "减少" style = removed_style else: mark = "无变化" style = normal_style # 计算分区中增大的TOP3路径(仅当分区增大时) top_notes = [] if diff > 0: path_diffs = [] all_paths = set(data1.keys()) | set(data2.keys()) for path in all_paths: size1 = data1.get(path, 0.0) size2 = data2.get(path, 0.0) path_diff = size1 - size2 if path_diff > 0: # 只记录增大的路径 path_diffs.append((path, path_diff)) # 按增大降序排序,取TOP3 path_diffs.sort(key=lambda x: x[1], reverse=True) for i, (path, diff_val) in enumerate(path_diffs[:3]): # 简化长路径显示 if len(path) > 50: path = "..." + path[-47:] top_notes.append(f"{i+1}. {path}: +{diff_val:.2f}MB") # 保存分区数据 overview_data.append({ 'name': partition_name, 'machine1': partition_total1, 'machine2': partition_total2, 'diff': diff, 'style': style, 'mark': mark, 'notes': "\n".join(top_notes) if top_notes else "无显著增大路径" }) # 写入行数据到总览页(新增备注列) ws_overview.write(current_row, 0, partition_name, style) ws_overview.write(current_row, 1, partition_total1, style) ws_overview.write(current_row, 2, partition_total2, style) ws_overview.write(current_row, 3, diff, style) ws_overview.write(current_row, 4, mark, style) ws_overview.write(current_row, 5, overview_data[-1]['notes'], wrap_style) current_row += 1 # 设置备注列宽度(100字符) ws_overview.col(5).width = 256 * 100 # 添加空行 current_row += 1 # 写入总计行(使用新的差计算方式) total_diff = total_machine1 - total_machine2 if total_diff > 0: total_mark = "总增加" # folder1 > folder2 total_style = added_style elif total_diff < 0: total_mark = "总减少" # folder1 < folder2 total_style = removed_style else: total_mark = "无变化" total_style = normal_style ws_overview.write(current_row, 0, "总计", header_style) ws_overview.write(current_row, 1, total_machine1, header_style) ws_overview.write(current_row, 2, total_machine2, header_style) ws_overview.write(current_row, 3, total_diff, header_style) ws_overview.write(current_row, 4, total_mark, header_style) # ====== 为每个文件创建单独的Sheet页 ====== # 获取所有唯一的文件名(两个文件夹的并集) all_filenames = sorted(set(machine1_all_files.keys()) | set(machine2_all_files.keys())) for filename in all_filenames: # 提取文件前缀 prefix = extract_file_prefix(filename) # 跳过无效前缀 if prefix not in PARTITION_NAME_MAP: continue # 获取分区名称 partition_name = PARTITION_NAME_MAP[prefix] # 创建Sheet页名称(文件名不带扩展名) sheet_name = filename.replace('.txt', '') if len(sheet_name) > 31: # Excel sheet名称长度限制 sheet_name = sheet_name[:31] # 创建Sheet页 ws = wb.add_sheet(sheet_name) print("创建文件Sheet页: {}".format(sheet_name)) # 当前行指针 current_row = 0 # 写入分区标题 title = "分区: {} - 文件: {}".format(partition_name, filename) ws.write_merge( current_row, current_row, 0, 5, title, title_style ) current_row += 1 # 写入文件夹名称(新格式) ws.write_merge(current_row, current_row, 0, 1, folder1_name, header_style) ws.write_merge(current_row, current_row, 2, 3, folder2_name, header_style) ws.write(current_row, 4, "差异(M)", header_style) ws.write(current_row, 5, "标记", header_style) current_row += 1 # 写入表头(新格式) headers = ['路径', '大小(M)', '路径', '大小(M)', '差异(M)', '标记'] for col, header in enumerate(headers): ws.write(current_row, col, header, header_style) current_row += 1 # 获取文件数据 data1 = machine1_all_files.get(filename, {}) data2 = machine2_all_files.get(filename, {}) # 获取所有路径(合并两个文件夹的路径) all_paths = sorted(set(data1.keys()) | set(data2.keys())) # 初始化变化统计数据 total_increase = 0.0 # 增大总和 total_decrease = 0.0 # 减小总和 total_added = 0.0 # 新增文件总和 total_removed = 0.0 # 去除文件总和 # 写入数据行(新格式) for path in all_paths: size1 = data1.get(path, 0.0) size2 = data2.get(path, 0.0) # 修改为 folder1 - folder2 的差计算方式 diff = size1 - size2 # 确定标记和样式(根据新的差计算方式调整) if size1 == 0 and size2 > 0: mark = "除去" cell_style = removed_style total_removed += size2 elif size1 > 0 and size2 == 0: mark = "新增" cell_style = added_style total_added += size1 else: if diff > 0: mark = "增大" cell_style = added_style total_increase += diff elif diff < 0: mark = "减小" cell_style = removed_style total_decrease += abs(diff) else: mark = "相同" cell_style = normal_style # 写入行数据(新格式) # folder1列 if size1 > 0: ws.write(current_row, 0, path, cell_style) ws.write(current_row, 1, size1, cell_style) else: ws.write(current_row, 0, "", cell_style) ws.write(current_row, 1, "", cell_style) # folder2列 if size2 > 0: ws.write(current_row, 2, path, cell_style) ws.write(current_row, 3, size2, cell_style) else: ws.write(current_row, 2, "", cell_style) ws.write(current_row, 3, "", cell_style) # 差异和标记列 ws.write(current_row, 4, diff, cell_style) ws.write(current_row, 5, mark, cell_style) current_row += 1 # 添加文件汇总行(新格式) file_total1 = sum(data1.values()) file_total2 = sum(data2.values()) file_diff = file_total1 - file_total2 # 写入汇总行(新格式) ws.write(current_row, 0, "文件汇总", header_style) ws.write(current_row, 1, file_total1, header_style) ws.write(current_row, 2, "", header_style) ws.write(current_row, 3, file_total2, header_style) ws.write(current_row, 4, file_diff, header_style) ws.write(current_row, 5, "", header_style) current_row += 1 # 添加变化分类统计行 - 修改为Python 2.7兼容的格式 message = ( u"{partition_name}路径下: " u"减小{total_decrease:.2f}M " u"增大{total_increase:.2f}M " u"新增文件{total_added:.2f}M " u"减少文件{total_removed:.2f}M" ).format( partition_name=partition_name, total_decrease=total_decrease, total_increase=total_increase, total_added=total_added, total_removed=total_removed ) ws.write_merge( current_row, current_row, 0, 5, message, summary_style ) # 保存文件 wb.save(output_xlsx) return "对比报告已成功生成: {}".format(output_xlsx) except Exception as e: import traceback traceback.print_exc() return "生成Excel文件时出错: {}".format(str(e)) def generate_single_report(folder, output_xlsx): """生成单机拆解报告""" # 获取文件夹名称用于显示 folder_name = os.path.basename(os.path.normpath(folder)) # 验证文件夹是否存在 if not os.path.exists(folder): print("错误: 目录不存在 - {}".format(folder)) return "目录 {} 不存在,请检查路径".format(folder) if not os.path.isdir(folder): print("错误: 路径不是目录 - {}".format(folder)) return "{} 不是有效目录".format(folder) # 初始化数据结构 main_data = {} # {prefix: {path: size}} 主文件数据 all_files = defaultdict(dict) # {filename: {path: size}} 所有文件数据 print("处理目录: {}".format(folder)) try: for filename in os.listdir(folder): if not filename.endswith('.txt'): continue # 提取文件前缀 prefix = extract_file_prefix(filename) # 跳过01_mount.txt和无效前缀 if prefix == '01_' or prefix not in PARTITION_NAME_MAP: continue file_path = os.path.join(folder, filename) partition_name = PARTITION_NAME_MAP[prefix] # 解析文件数据 file_data = parse_du_file(file_path) # 添加到所有文件数据 all_files[filename] = file_data # 如果是主文件,添加到主文件数据(用于总览页) if is_main_partition_file(filename, prefix): print("解析主分区文件: {}".format(file_path)) main_data[prefix] = file_data except OSError as e: print("目录访问错误: {}".format(str(e))) return "无法访问目录 {}: {}".format(folder, str(e)) # 创建Excel工作簿 try: wb = xlwt.Workbook(encoding='utf-8') header_style = xlwt.easyxf('font: bold on') title_style = xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre') normal_style = xlwt.easyxf() # ====== 创建总览Sheet页(只包含主文件数据) ====== ws_overview = wb.add_sheet('总览') print("创建总览Sheet页(仅主文件数据)") # 当前行指针 current_row = 0 # 写入总览标题 ws_overview.write_merge( current_row, current_row, 0, 1, "存储使用总览(仅主分区文件) - 单机报告", title_style ) current_row += 1 # 写入文件夹名称 ws_overview.write(current_row, 0, "分区", header_style) ws_overview.write(current_row, 1, "总大小(MB)", header_style) current_row += 1 # 存储各分区汇总数据 total_size = 0.0 # 按分区顺序处理数据 for prefix in sorted(PARTITION_NAME_MAP.keys()): partition_name = PARTITION_NAME_MAP[prefix] # 跳过data分区 if partition_name == 'data': continue # 获取主文件数据 data = main_data.get(prefix, {}) partition_total = round(sum(data.values()), 2) total_size += partition_total # 写入行数据到总览页 ws_overview.write(current_row, 0, partition_name, normal_style) ws_overview.write(current_row, 1, partition_total, normal_style) current_row += 1 # 添加空行 current_row += 1 # 写入总计行 ws_overview.write(current_row, 0, "总计", header_style) ws_overview.write(current_row, 1, total_size, header_style) # ====== 为每个文件创建单独的Sheet页 ====== for filename in sorted(all_files.keys()): # 提取文件前缀 prefix = extract_file_prefix(filename) # 跳过无效前缀 if prefix not in PARTITION_NAME_MAP: continue # 获取分区名称 partition_name = PARTITION_NAME_MAP[prefix] # 创建Sheet页名称(文件名不带扩展名) sheet_name = filename.replace('.txt', '') if len(sheet_name) > 31: # Excel sheet名称长度限制 sheet_name = sheet_name[:31] # 创建Sheet页 ws = wb.add_sheet(sheet_name) print("创建文件Sheet页: {}".format(sheet_name)) # 当前行指针 current_row = 0 # 写入分区标题 title = "分区: {} - 文件: {}".format(partition_name, filename) ws.write_merge( current_row, current_row, 0, 1, title, title_style ) current_row += 1 # 写入表头 headers = ['路径', '大小(M)'] for col, header in enumerate(headers): ws.write(current_row, col, header, header_style) current_row += 1 # 获取文件数据 data = all_files[filename] # 按路径排序 sorted_paths = sorted(data.keys()) # 写入数据行 for path in sorted_paths: size = data[path] ws.write(current_row, 0, path, normal_style) ws.write(current_row, 1, size, normal_style) current_row += 1 # 写入汇总行 file_total = sum(data.values()) ws.write(current_row, 0, "文件汇总", header_style) ws.write(current_row, 1, file_total, header_style) # 保存文件 wb.save(output_xlsx) return "单机报告已成功生成: {}".format(output_xlsx) except Exception as e: import traceback traceback.print_exc() return "生成Excel文件时出错: {}".format(str(e)) if __name__ == "__main__": # 创建参数解析器 parser = argparse.ArgumentParser(description='存储空间分析工具') subparsers = parser.add_subparsers(dest='mode', help='运行模式') # 双机对比模式 dual_parser = subparsers.add_parser('dual', help='双机对比模式') dual_parser.add_argument('folder1', help='第一个文件夹路径') dual_parser.add_argument('folder2', help='第二个文件夹路径') dual_parser.add_argument('output', help='输出Excel文件路径') # 单机拆解模式 single_parser = subparsers.add_parser('single', help='单机拆解模式') single_parser.add_argument('folder', help='待分析文件夹路径') single_parser.add_argument('output', help='输出Excel文件路径') # 解析参数 args = parser.parse_args() if args.mode == 'dual': print("运行双机对比模式...") result = generate_dual_report(args.folder1, args.folder2, args.output) elif args.mode == 'single': print("运行单机拆解模式...") result = generate_single_report(args.folder, args.output) else: result = "错误:请选择 'dual' 或 'single' 模式" print(result)V27960021@dg03podv27960021kj4p:~/存储/hawaii$ python storage.py dual ./22610/存储/测试机 ./22610/存储/对比机 22610_report_dual.xlsx File "storage.py", line 222 top_notes.append(f"{i+1}. {path}: +{diff_val:.2f}MB") ^ SyntaxError: invalid syntax
07-16
<think>这个错误通常发生在尝试在一个无效的图形句柄上操作时。根据错误信息,问题出现在`saveas`函数调用时,可能因为图形窗口已经被关闭或未正确初始化。让我们修改可视化函数以避免此错误。 ### 解决方案:增强可视化函数的健壮性 ```matlab function visualize_path(image_path, path, start, goal_reached, threshold) % 加载图像 img = imread(image_path); % 创建新图形并保存句柄 fig = figure('Position', [100, 100, 800, 600], 'Visible', 'off'); % 先不显示,最后统一显示 try imshow(img); hold on; title(sprintf('A*路径规划 (阈: %d)', threshold), 'FontSize', 14); % 绘制起点 plot(start(2), start(1), 'o', 'MarkerSize', 12, ... 'MarkerFaceColor', 'g', 'MarkerEdgeColor', 'k', 'LineWidth', 2); % 获取并绘制可行驶区域(浅灰色背景) if size(img, 3) == 3 gray_img = im2gray(img); else gray_img = img; end [free_rows, free_cols] = find(gray_img < threshold); scatter(free_cols, free_rows, 3, [0.9, 0.9, 0.9], 'filled'); % 绘制路径(如果存在) if ~isempty(path) % 主路径 plot(path(:,2), path(:,1), 'b-', 'LineWidth', 3); % 终点标记 plot(goal_reached(2), goal_reached(1), 's', 'MarkerSize', 16, ... 'MarkerFaceColor', 'm', 'MarkerEdgeColor', 'k', 'LineWidth', 2); % 图例 legend({'起点', '可行驶区域', '路径', '到达终点'}, ... 'Location', 'best', 'FontSize', 10); % 路径长度计算 if size(path,1) > 1 path_length = sum(sqrt(sum(diff(path).^2, 2))); annotation('textbox', [0.6, 0.02, 0.3, 0.05], ... 'String', sprintf('路径长度: %.2f px', path_length), ... 'FitBoxToText', 'on', 'BackgroundColor', 'w', 'FontSize', 10); end else legend({'起点', '可行驶区域'}, 'Location', 'best'); title('未找到有效路径', 'Color', 'r'); end % 添加网格 grid on; set(gca, 'GridColor', [0.2, 0.2, 0.2], 'GridAlpha', 0.3); % 现在显示图形 set(fig, 'Visible', 'on'); % 尝试保存图像 try saveas(fig, 'path_planning_result.png'); disp('结果已保存为 path_planning_result.png'); catch ME_save warning('无法保存图像: %s', ME_save.message); end catch ME % 关闭无效图形 if ishandle(fig) close(fig); end rethrow(ME); end end ``` ### 关键修改点 1. **图形创建方式修改** ```matlab fig = figure('Position', [100, 100, 800, 600], 'Visible', 'off'); ``` - 先创建不可见图形,完成所有绘图操作后再显示 - 避免绘图中途用户关闭窗口导致句柄失效 2. **添加异常捕获** ```matlab try % 绘图操作... catch ME if ishandle(fig) close(fig); end rethrow(ME); end ``` - 捕获绘图过程中的错误 - 确保异常时关闭无效图形 3. **安全保存机制** ```matlab try saveas(fig, 'path_planning_result.png'); catch ME_save warning('无法保存图像: %s', ME_save.message); end ``` - 捕获保存时可能发生的错误(如写入权限问题) - 不影响主流程,仅显示警告 4. **路径长度计算保护** ```matlab if size(path,1) > 1 path_length = sum(sqrt(sum(diff(path).^2, 2))); % 添加标注... end ``` - 避免单点路径计算导致的`diff`错误 ### 常见错误及解决方法 1. **错误:图形对象无效** - **原因**:图形窗口被提前关闭 - **解决**:使用`'Visible','off'`创建窗口,完成绘制再显示 2. **错误:保存失败** - **原因**:无写入权限或路径无效 - **解决**:使用绝对路径保存,如: ```matlab saveas(fig, fullfile(pwd, 'results', 'path_planning_result.png')); ``` 3. **错误:索引超出范围** - **原因**:起点坐标超出图像范围 - **解决**:在调用函数前验证坐标有效性 ```matlab img = imread(image_path); if start(1) > size(img,1) || start(2) > size(img,2) error('起点坐标超出图像范围'); end ``` ### 完善的主脚本示例 ```matlab %% === 主脚本 A_ex.m === clear; clc; close all; % 1. 参数设置 image_path = 'office_map.png'; start_point = [150, 120]; num_goals = 5; gray_threshold = 210; % 2. 添加当前目录到路径 (确保能找到函数) addpath(fileparts(mfilename('fullpath'))); % 3. 图像预加载验证 img_info = imfinfo(image_path); fprintf('图像尺寸: %d x %d\n', img_info.Height, img_info.Width); % 4. 起点坐标验证 if any(start_point < 1) || start_point(1) > img_info.Height || start_point(2) > img_info.Width error('起点坐标超出图像边界'); end % 5. 执行路径规划 [path, goal] = image_based_astar(image_path, start_point, num_goals, gray_threshold); % 6. 可视化 visualize_path(image_path, path, start_point, goal, gray_threshold); ``` ### 最终建议 - 对于大型项目,使用MATLAB的面向对象编程封装路径规划模块 - 考虑将地图预处理和路径规划分离,提高重用性 §§相关问题§§: 1. 如何实现实时更新的动态障碍物避障? 2. 如何将A*算法与D* Lite算法结合用于动态环境? 3. 怎样使用GPU加速大规模网格地图的路径规划? 4. 如何生成平滑的Bézier曲线路径替代网格路径? </think>### 错误分析与解决方案:图形对象无效 这个错误通常发生在图形对象被意外关闭或删除后再尝试操作时。在您的代码中,问题最可能出现在 `saveas` 函数调用时,因为此时图形窗口可能已被关闭或处于无效状态。 ### 修复后的可视化函数 ```matlab function visualize_path(image_path, path, start, goal_reached, threshold) % 加载图像 img = imread(image_path); % 创建新图形并获取句柄 fig_handle = figure('Units', 'normalized', 'Position', [0.1, 0.1, 0.8, 0.8], ... 'Name', 'A*路径规划结果', 'NumberTitle', 'off', 'Visible', 'on'); try % 显示图像 imshow(img); hold on; title(sprintf('A*路径规划 (阈: %d)', threshold), 'FontSize', 14); % ===== 增强的图形对象创建 ===== % 1. 绘制起点 (确保创建图形对象) start_plot = plot(start(2), start(1), 'o', 'MarkerSize', 12, ... 'MarkerFaceColor', 'g', 'MarkerEdgeColor', 'k', 'LineWidth', 2); % 2. 获取并绘制可行驶区域 if size(img, 3) == 3 gray_img = im2gray(img); else gray_img = img; end [free_rows, free_cols] = find(gray_img < threshold); % 使用scatter代替plot避免过多图形对象 area_plot = scatter(free_cols, free_rows, 3, [0.9, 0.9, 0.9], 'filled'); area_plot.AlphaData = 0.2; % 设置透明度 % 3. 绘制路径和终点 if ~isempty(path) % 创建路径图形对象 path_plot = plot(path(:,2), path(:,1), 'b-', 'LineWidth', 2); % 创建终点图形对象 goal_plot = plot(goal_reached(2), goal_reached(1), 's', 'MarkerSize', 14, ... 'MarkerFaceColor', 'm', 'MarkerEdgeColor', 'k', 'LineWidth', 2); % 添加图例 (明确指定图形对象) legend([start_plot, area_plot, path_plot, goal_plot], ... {'起点', '可行驶区域', '路径', '到达终点'}, ... 'Location', 'best', 'FontSize', 10); % 添加路径长度信息 if size(path, 1) > 1 path_length = sum(sqrt(sum(diff(path).^2, 2))); text(0.02, 0.98, sprintf('路径长度: %.2f px', path_length), ... 'Units', 'normalized', 'FontSize', 10, ... 'BackgroundColor', 'w', 'VerticalAlignment', 'top'); end else legend([start_plot, area_plot], {'起点', '可行驶区域'}, 'Location', 'best'); title('未找到有效路径', 'Color', 'r'); end % ===== 增强的图形保存 ===== % 确保图形窗口仍然打开且有效 if isgraphics(fig_handle) && isvalid(fig_handle) try % 方法1: 使用exportgraphics (推荐R2020a及以上) exportgraphics(fig_handle, 'path_planning_result.png', 'Resolution', 300); disp('结果已保存为: path_planning_result.png'); catch % 方法2: 传统saveas备份方案 saveas(fig_handle, 'path_planning_result.png'); end else warning('图形窗口已关闭,无法保存结果'); end % 添加网格 grid on; set(gca, 'GridColor', [0.2, 0.2, 0.2], 'GridAlpha', 0.3); catch ME % 错误处理 fprintf(2, '可视化错误: %s\n', ME.message); % 尝试保存当前图形状态 if exist('fig_handle', 'var') && isgraphics(fig_handle) && isvalid(fig_handle) try saveas(fig_handle, 'error_snapshot.png'); fprintf('错误截图已保存为: error_snapshot.png\n'); catch fprintf(2, '无法保存错误截图\n'); end end % 重新抛出错误以便主程序处理 rethrow(ME); end end ``` ### 关键修复点解析 1. **图形句柄管理** ```matlab fig_handle = figure(...); % 显式获取图形句柄 ``` - 创建图形时显式获取并存储句柄 - 使用 `'Visible','on'` 确保窗口可见 2. **图形对象有效性检查** ```matlab if isgraphics(fig_handle) && isvalid(fig_handle) % 执行图形操作 end ``` - 在执行任何操作前检查图形是否有效 - `isgraphics` 检查是否为图形对象,`isvalid` 检查是否未被删除 3. **增强的错误处理** ```matlab try % 绘图操作 catch ME % 错误处理和截图保存 end ``` - 捕获绘图过程中的任何错误 - 尝试保存当前状态用于调试 4. **安全的保存机制** ```matlab exportgraphics(fig_handle, 'path_planning_result.png', 'Resolution', 300); ``` - 优先使用 `exportgraphics` (MATLAB R2020a+) - 分辨率更高,错误率更低 - 保留传统 `saveas` 作为备选方案 5. **图形对象显式引用** ```matlab start_plot = plot(...); % 存储图形对象 legend([start_plot, ...], ...); % 显式引用对象 ``` - 避免隐式依赖当前坐标轴 - 防止图例引用错误对象 ### 主脚本增强版 ```matlab %% === 主脚本 A_ex.m === clear all; close all; clc; % 1. 设置参数 image_path = 'office_map.png'; % 确保文件存在 start_point = [150, 120]; % 起点坐标 (行, 列) num_goals = 5; % 终点数量 gray_threshold = 210; % 灰度阈 (0-255) % 2. 验证图像文件存在 if ~isfile(image_path) error('图像文件未找到: %s', image_path); end % 3. 预加载图像验证尺寸 try img_info = imfinfo(image_path); fprintf('图像尺寸: %d x %d px\n', img_info.Height, img_info.Width); % 验证起点在图像范围内 if start_point(1) > img_info.Height || start_point(2) > img_info.Width error('起点坐标超出图像范围'); end catch ME error('图像读取失败: %s', ME.message); end % 4. 执行路径规划 [path, goal_reached] = image_based_astar(image_path, start_point, num_goals, gray_threshold); % 5. 可视化结果 if exist('visualize_path', 'file') visualize_path(image_path, path, start_point, goal_reached, gray_threshold); else fprintf(2, '可视化函数未找到,绘制简单结果\n'); figure; img = imread(image_path); imshow(img); hold on; plot(start_point(2), start_point(1), 'go', 'MarkerSize', 12, 'LineWidth', 2); if ~isempty(path) plot(path(:,2), path(:,1), 'b-', 'LineWidth', 2); plot(goal_reached(2), goal_reached(1), 'rx', 'MarkerSize', 14, 'LineWidth', 2); end title('A*路径规划结果'); end ``` ### 预防性措施总结 1. **图形对象生命周期管理** - 始终显式存储图形句柄 - 操作前检查对象有效性 - 使用 `isgraphics` 和 `isvalid` 函数 2. **保存操作最佳实践** ```matlab % 推荐方式 (R2020a+) exportgraphics(fig_handle, 'output.png', 'Resolution', 300); % 兼容方式 saveas(fig_handle, 'output.png'); ``` 3. **错误处理金字塔** ```matlab try % 主要操作 catch ME % 1. 记录错误 fprintf(2, '错误: %s\n', ME.message); % 2. 尝试恢复操作 if condition retry_operation(); end % 3. 保存调试信息 save_debug_data(); end ``` 4. **图形对象创建规范** - 显式存储每个图形对象的句柄 - 避免依赖 MATLAB 的当前坐标轴状态 - 使用 `drawnow` 强制渲染更新 ### 常见问题解决指南 1. **"图形对象无效"错误** - **原因**: 图形窗口被手动关闭 - **解决**: 在脚本开头添加 `close all force` 强制关闭所有图形 2. **保存空白图像** - **原因**: 在渲染完成前保存 - **解决**: 在保存前添加 `drawnow expose` 3. **图例显示错误** - **原因**: 隐式引用错误对象 - **解决**: 显式传递图形对象到图例 4. **高分辨率保存问题** ```matlab % 最佳高清保存方案 exportgraphics(fig, 'high_res.png', 'Resolution', 600); % 备选方案 print(fig, '-dpng', '-r600', 'high_res.png'); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值