boost — застрял в написании моей разностной утилиты в Stack Overflow

Я пытался использовать то, что я узнал об обработке файлов и ресурсов в C ++: я хотел бы написать diff-подобная утилита.

Вот моя последняя версия

#include <iostream>
#include <cstdlib>
#include <fstream>

int main(int argc, char* argv[])
{
if(argc!=3)
{
std::cout << "error: 2 arguments required, now exiting ..." << std::endl;
exit (EXIT_FAILURE);
}

std::ifstream file_1(argv[1]);
std::ifstream file_2(argv[2]);

if( file_1.fail() || file_2.fail() )
{
std::cout << "error: can't open files, now exiting ..." << std::endl;
exit (EXIT_FAILURE);
}

std::string dummy_1;
std::string dummy_2;

while(!file_1.eof()) // dummy condition
{
std::getline(file_1,dummy_1);
std::getline(file_2,dummy_2);
std::cout << ((dummy_1==dummy_2) ? "= " : "# ") << dummy_1 << std::endl << "  " << dummy_2 << std::endl;
}

return(0);
}

Это мои рекомендации:

  • сравнить 2 файла
  • пользователь должен передать имена этих 2 файлов непосредственно в исполняемый файл, только эти 2 аргумента
  • покрыть как можно больше обработка ошибок насколько это возможно в C ++
  • старайтесь избегать шагов, специфичных для платформы или непереносимого кода

Моя настоящая проблема в том, что я не знаю, как улучшить свои пустышка эффективно.
На данный момент итерация while просто следует длине первого переданного файла, и я хотел бы однозначно пройти весь путь вниз в обоих файлах и решить эту проблему, не вводя излишнее количество слов, как дополнительный цикл, чтобы получить и сравнить длину этих двух файлов до того, как делать реальное сравнение.

Я также хотел бы знать, можно ли считать мой подход безопасным.

В конце концов я мог бы также принять ответы, предлагающие решение с библиотеками boost, поскольку они довольно переносимы, и я уже знаю, что буду использовать их по другим причинам.

Благодарю.

2

Решение

Я начал с написания довольно длинного комментария к ответу @Loki Astari, но он достаточно длинный (и, IMO, достаточно чёткий способ сделать работу), что он, вероятно, имеет больше смысла как независимый ответ. В этом случае вам нужно что-то близкое к стандартному циклу, за исключением того, что вы продолжаете чтение до тех пор, пока чтение из одного из файлов будет успешным. В таком случае @john прав, и лучше избегать использования eof() как часть условия цикла.

std::string line1, line2;
static const char *prefixes[] = {"#  ", "=  "};


while (std :: getline (file_1, line1) || std :: getline (file_2, line2))
станд :: соиЬ << Префиксы [line1 == строка2] << строка 1 << «\ n» << line2 << «\ П»;

Edit: @ user1802174 поднял хороший вопрос — как это было, цикл на самом деле вообще не считывал данные параллельно. Так как он использовал || который выполняет оценку короткого замыкания, когда / если считывание из первого файла прошло успешно, он ничего не прочитал из второго файла. К счастью, он ошибся в одном: это довольно легко исправить. По крайней мере, в этом случае + работает нормально, хотя мы должны явно привести результат к bool, Я также добавил исправление для того факта, что при неудаче getline оставляет предыдущее содержимое строки без изменений, поэтому нам нужно явно очищать строки на каждой итерации цикла, чтобы получить желаемое поведение.

while (line1.clear(), line2.clear(),
(bool)std::getline(file_1, line1) + (bool)std::getline(file_2, line2))
{
std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
}

На этот раз я сделал быстрый тест. Файл 1:

line1
line 2

Файл 2:

line 1
line 2
line 3

результат:

#  line1
line 1
=  line 2
line 2
#
line 3

Хотя очевидно, что утилита различий не является полноценной, я думаю, что она делает то, что планировалось.

Как и в ответе @Loki Astari, в основном это будет действовать так, как если бы файл с меньшим количеством строк был заполнен таким количеством пустых строк в конце, которое необходимо для соответствия более длинному файлу.

Кроме того, обратите внимание на использование "\n" вместо std::endl, Наряду со вставкой новой строки, std::endl также очищает выходной буфер, который вам почти наверняка не нужен в этом случае. Очистка буфера все еще дает правильные результаты, но во многих случаях, вероятно, будет происходить намного медленнее.

Изменить: Что касается стиля кодирования, это, вероятно, является немного лучше написать цикл как for цикл вместо while:

for ( ; (bool)std::getline(file_1, line1) + (bool)std::getline(file_2, line2))
; line1.clear(), line2.clear())
{
std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
}

Я лично вижу небольшую реальную выгоду от использования приведений стиля C ++ здесь. Если бы я хотел уйти от использования (bool)Я, вероятно, использовал бы другую известную идиому (которая, по общему признанию, многим также не нравится):

for ( ; !!std::getline(file_1, line1) + !!std::getline(file_2, line2))
; line1.clear(), line2.clear())
{
std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
}

Если кто-то действительно объекты с использованием оператора запятой, это легко переписать как:

while (!!std::getline(file_1, line1) + !!std::getline(file_2, line2))
{
std::cout << prefixes[line1==line2] << line1 << "\n   " << line2 << "\n";
line1.clear();
line2.clear();
}

Лично я не считаю это улучшением, но другие могут не согласиться.

3

Другие решения

Как указал Джон. Использование eof () в условии обычно неправильно.

Но в этом случае я думаю, что это уместно. Но в результате вам нужно добавить несколько дополнительных проверок.

while(true)  // exit provided by break.
{
std::string dummy_1;   // By declaring them here you force them to be
std::string dummy_2;   // reset each iteration.

// Because you are doing the read inside the loop
// You need to check if the reads work.
if (!std::getline(file_1,dummy_1) && !std::getline(file_2,dummy_2))
{
// Only exit if both reads fail.
break;
}

// Got here if at least one read worked.
// A failed read will result in an empty line for comparison.
std::cout << ((dummy_1==dummy_2) ? "= " : "# ") << dummy_1 << std::endl << "  " << dummy_2 << std::endl;
}
3

По-прежнему eof() это не то, что нужно делать. Это работает

while (std::getline(file_1, dummy_1) && std::getline(file_2, dummy_2))
{
...
}

Предлагаю вам прочитать о том, что eof() действительно делает. это не то, что вы думаете, но на самом деле это будет полезно в этой программе, потому что вы можете использовать его надлежащим образом, чтобы сказать, какой из ваших двух файлов попал в конец файла. Увидеть Вот

Вы можете на самом деле использовать eof() Правильно в этой программе выяснить, какой из двух файлов попал в конец файла. Я бы наверное написал твой цикл примерно так

for (;;)
{
getline(file_1, dummy_1);
getline(file_2, dummy_2);
if (file_1.eof() || file_2.eof())
break;
...
}
if (file_1.eof() && file_2.eof())
{
// both at end of file
}
else if (file_1.eof())
{
// file 1 at end of file
}
else
{
// file 2 at end of file
}

Обратите внимание, что eof() тест приходит после getline(), не раньше, чем. Вот как eof() должен быть использован.

3
По вопросам рекламы [email protected]