C++文件对比程序实现问题:无法定位原文件真实差异行列
解决文件对比中移除空格后的真实行列定位问题
问题背景
现有C++文件对比程序会移除空格后对比内容,但无法正确定位带换行的原文件中差异的真实行列位置。例如fileTwo.txt包含多行内容,程序输出的差异位置是移除空格后的单行行列,而非原文件的第50行第26列。
问题根源
程序直接基于移除空格后的字符串统计换行和计算列数,但原文件中的换行符和空格已被过滤,导致无法关联到原文件的真实位置。需要为每个保留的非空格字符建立与原文件行列的映射关系。
修改后的完整代码
#include <iostream> #include <fstream> #include <sstream> #include <algorithm> #include <cctype> #include <string> #include <vector> #include <utility> // 保存字符在原文件中的行列位置 struct Position { size_t line; size_t column; }; // 移除空格并生成每个非空格字符对应的原文件位置映射 std::pair<std::string, std::vector<Position>> removeSpacesWithMapping(const std::string& original) { std::string result; std::vector<Position> positions; size_t currentLine = 1; size_t currentColumn = 1; for (char c : original) { if (std::isspace(static_cast<unsigned char>(c))) { if (c == '\n') { currentLine++; currentColumn = 1; } else { currentColumn++; } continue; } result += c; positions.push_back({currentLine, currentColumn}); currentColumn++; } return {result, positions}; } // 找到两个字符串的第一个差异位置,并返回原文件的真实行列 size_t findFirstDifference(const std::string& str1, const std::string& str2, const std::vector<Position>& posMap1, const std::vector<Position>& posMap2, size_t& line1, size_t& column1, size_t& line2, size_t& column2) { size_t minSize = std::min(str1.size(), str2.size()); for (size_t i = 0; i < minSize; ++i) { if (str1[i] != str2[i]) { const Position& p1 = posMap1[i]; const Position& p2 = posMap2[i]; line1 = p1.line; column1 = p1.column; line2 = p2.line; column2 = p2.column; return i; } } // 处理其中一个字符串更长的情况 if (str1.size() > minSize) { const Position& p1 = posMap1[minSize]; line1 = p1.line; column1 = p1.column; line2 = posMap2.back().line; column2 = posMap2.back().column + 1; } else if (str2.size() > minSize) { const Position& p2 = posMap2[minSize]; line2 = p2.line; column2 = p2.column; line1 = posMap1.back().line; column1 = posMap1.back().column + 1; } else { line1 = posMap1.back().line; column1 = posMap1.back().column; line2 = posMap2.back().line; column2 = posMap2.back().column; } return minSize; } // 显示差异附近的内容 std::string displayDifferenceOccurrence(const std::string& str, size_t position) { size_t displayStart = (position > 10) ? (position - 10) : 0; size_t displayEnd = std::min(position + 50, str.size()); return str.substr(displayStart, displayEnd - displayStart); } int main(int argc, char *argv[]) { if (argc != 3) { std::cerr << "Usage: " << argv[0] << " <file1> <file2>\n"; return 1; } std::ifstream file1(argv[1]); if (!file1.is_open()) { std::cerr << "Error opening " << argv[1] << "\n"; return 1; } std::ifstream file2(argv[2]); if (!file2.is_open()) { std::cerr << "Error opening " << argv[2] << "\n"; return 1; } // 读取原文件完整内容 std::stringstream buffer1, buffer2; buffer1 << file1.rdbuf(); std::string original1 = buffer1.str(); buffer2 << file2.rdbuf(); std::string original2 = buffer2.str(); // 移除空格并获取位置映射 auto [content1, posMap1] = removeSpacesWithMapping(original1); auto [content2, posMap2] = removeSpacesWithMapping(original2); size_t line1 = 1, column1 = 1, line2 = 1, column2 = 1; size_t firstDifference = findFirstDifference(content1, content2, posMap1, posMap2, line1, column1, line2, column2); if (firstDifference == content1.size() && firstDifference == content2.size()) { std::cout << "**********************************************************************************\n"; std::cout << "The contents of " << argv[1] << " and " << argv[2] << " are the same.\n"; } else { std::cout << "**********************************************************************************\n"; std::cout << "The contents of " << argv[1] << " and " << argv[2] << " are different.\n"; std::cout << "\nDifference at " << argv[1] << " line " << line1 << ", column " << column1 << "\n"; std::string displayContent1 = displayDifferenceOccurrence(content1, firstDifference); std::cout << "\n" << argv[1] << " occurrence: \n" << displayContent1 << "\n"; std::cout << " ^\n"; std::cout << "\nDifference at " << argv[2] << " line " << line2 << ", column " << column2 << "\n"; std::string displayContent2 = displayDifferenceOccurrence(content2, firstDifference); std::cout << "\n" << argv[2] << " occurrence: \n" << displayContent2 << "\n"; std::cout << " ^\n"; } file1.close(); file2.close(); return 0; }
关键修改说明
- 新增位置映射功能:
removeSpacesWithMapping函数遍历原文件内容,过滤空格的同时,记录每个保留字符在原文件中的行列位置,生成映射表。 - 基于映射表获取真实位置:
findFirstDifference函数不再直接统计过滤后的字符串换行,而是通过映射表直接获取差异字符在原文件的真实行列。 - 处理字符串长度不一致的边界情况:当其中一个文件内容更长时,通过映射表末尾的位置确定差异的结束位置。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

