You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

关键修改说明

  1. 新增位置映射功能:removeSpacesWithMapping函数遍历原文件内容,过滤空格的同时,记录每个保留字符在原文件中的行列位置,生成映射表。
  2. 基于映射表获取真实位置:findFirstDifference函数不再直接统计过滤后的字符串换行,而是通过映射表直接获取差异字符在原文件的真实行列。
  3. 处理字符串长度不一致的边界情况:当其中一个文件内容更长时,通过映射表末尾的位置确定差异的结束位置。

内容的提问来源于stack exchange,提问作者John

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 23:02:02