Сравнение списков
Итак, плюсики, сравнение строк двух разных файлов.
Вы все прекрасно знаете как я обожаю креветки и пиво практические, а не выдуманные задачи.
Потому можете себе представить как я был воодушевлен, что мне, наконец-то можно будет размять пальцы за настоящей, а не выдуманной задачей.
Собственно, задача легчайшая, но я подумал, что сначала решу её тупо, в лоб, а потом посижу - покумекаю как это можно сделать красиво.
Собственно к телу делу. Листая с утра пикабу, наткнулся на пост пользователя, которому, с его слов, нужно было автоматизировать процесс сравнения строк в двух файлах так, чтобы на выходе были только уникальные, неповторяющиеся снежинки строки, а мерзкие повторюшки — отправлялись в биореактор. Утренняя прогулка не успела закончиться, а я уже прикидывал как это сделать: вычитать список из одного файла и затем построчно сравнить с другим, а потом наоборот? Вычистить массив придётся, да и переоткрывать файл... Смысл заморачиваться, примерный алгоритм уже маячил в моём ещё не до конца проснувшемся мозгу. Сколько там могут эти текстовики весить? Учитывая современные объемы оперативной памяти — не думаю, что стоит заморачиваться, по крайней мере пока. Так что делаем так:
1. Самое главное! Ничего стирать не будем, просто на выходе будут 4 файла, а не два.
2. Вычитываем строки из обоих файлов (пока).
3. Сравниваем строки.
4. Если строки равны (==), нихера не делаем.
5. В противном случае — записываем их в черный список соответствующие файлы.
Собственно, код:
#include <fstream>
#include <string>
#include <set>
int main() {
std::ifstream file1("file1.txt");
std::ifstream file2("file2.txt");
std::ofstream file3("file3.txt", std::ios_base::app);
std::ofstream file4("file4.txt", std::ios_base::app);
std::set<std::string> set1, set2;
std::string line;
// Считываем строки из первого файла и добавляем их в set1
while (std::getline(file1, line)) {
set1.insert(line);
}
// Считываем строки из второго файла и добавляем их в set2
while (std::getline(file2, line)) {
set2.insert(line);
}
// Записываем уникальные строки из первого файла в file3
for (const auto& str : set1) {
if (set2.find(str) == set2.end()) { // Проверяем, отсутствует ли строка во втором файле
file3 << str << std::endl; // Записываем уникальную строку
}
}
// Записываем уникальные строки из второго файла в file4
for (const auto& str : set2) {
if (set1.find(str) == set1.end()) { // Проверяем, отсутствует ли строка в первом файле
file4 << str << std::endl; // Записываем уникальную строку
}
}
// Закрываем файлы
file1.close();
file2.close();
file3.close();
file4.close();
return 0;
}
Ссыкло на порнхаб ссылко на гитхаб.
Пока так, чуть позже мы с ним ещё поизвращаемся, поэкономим память, попробуем асинхронщину, она тут применима.
upd (ver 1.1) подумаем об экономии памяти
Представим что у нас реально большущие наборы данных, окей, тогда давайте выгружать для проверки только 1 список, тогда нам нужно будет:
1. Выгрузить список для проверки
2. Сверить его со вторым
3. Попутно записывая уникальных в выходной файл
4. По окончании проверки — файл закрыть, список очистить
5. Составить список из второго файла
6. Повторить действия 2-4, но для файла за номером
Окей, говно вопрос, кода, конечно, больше, делать медленнее, но экономим память, за счет считывания всего 1 списка (сета):
#include <iostream>
#include <fstream>
#include <string>
#include <set>
int main() {
std::ifstream file1("file1.txt");
std::ifstream file2("file2.txt");
std::ofstream outputFile1("unique_from_file1.txt", std::ios::app);
if (!file1.is_open() || !file2.is_open()) {
std::cerr << "opening error" << std::endl;
return 1;
}
// Сохраним строки второго файла в set
std::set<std::string> linesInFile;
std::string line;
while (std::getline(file2, line)) {
linesInFile.insert(line);
}
// Сравниваем строки из первого файла с множеством строк из второго файла
while (std::getline(file1, line)) {
if (linesInFile.find(line) == linesInFile.end()) {
outputFile1 << line << std::endl; // Записываем уникальную строку
}
}
//закрываем файл 3, открываем файл 4, попутно чистим временный список
outputFile1.close();
linesInFile.clear();
std::ofstream outputFile2("unique_from_file2.txt", std::ios::app);
file2.clear(); // Очищаем флаг конца файла
file2.seekg(0); // Возвращаемся в начало второго файла
file1.clear(); // Очищаем флаг конца файла
file1.seekg(0); // Возвращаемся в начало первого файла
while (std::getline(file1, line)) {
linesInFile.insert(line);
}
// Сравниваем строки из первого файла с множеством строк из второго файла
while (std::getline(file2, line)) {
if (linesInFile.find(line) == linesInFile.end()) {
outputFile2 << line << std::endl; // Записываем уникальную строку
}
}
while (std::getline(file2, line)) {
std::cout << line << std::endl;
}
// Закрываем файлы
file1.close();
file2.close();
outputFile2.close();
std::cout << "all done" << std::endl;
return 0;
}
upd2 Представим, что мы бомжи. Или что нас за лишнюю купленную плашку оперативки ебут медведи.
Итак, ради максимальной экономии памяти, забьем процессорное время и остальное, будем открывать и закрывать файлы при каждом чихе, лишь бы память не перерасходовать, важно экономить память, ещё можно заиспользовать unsortered_set, и move, чтобы избежать лишних копий строк.
Итак, понгали:
#include <iostream>
#include <fstream>
#include <string>
#include <unordered_set>
int main() {
std::ifstream file1("file1.txt");
std::ifstream file2("file2.txt");
std::ofstream outputFile1("unique_from_file1.txt", std::ios::app);
if (!file1.is_open() || !file2.is_open() || !outputFile1.is_open() || !outputFile2.is_open()) {
std::cerr << "3rr0r. you dolbaeb" << std::endl;
return 1;
}
// Сохраним строки второго файла в unordered_set для быстрого поиска
std::unordered_set<std::string> linesInFile;
std::string line;
while (std::getline(file2, line)) {
linesInFile.insert(std::move(line)); // Применяем move, чтобы избежать лишнего копирования
}
file2.close(); // Закрываем второй файл для освобождения ресурсов
// Сравниваем строки из первого файла
while (std::getline(file1, line)) {
if (linesInFile.find(line) == linesInFile.end()) {
outputFile1 << line << std::endl; // Записываем уникальную строку
}
}
outputFile1.close(); // Закрываем файл 3
// Открываем файл 4 и очищаем временный список
std::ofstream outputFile2("unique_from_file2.txt", std::ios::app);
linesInFile.clear();
file1.clear(); // Очищаем флаг конца файла
file1.seekg(0); // Возвращаемся к началу первого файла
while (std::getline(file1, line)) {
linesInFile.insert(std::move(line)); // Опять используем move
}
file1.close(); // Закрываем первый файл
// Открываем файл 2 снова для чтения
file2.open("file2.txt");
while (std::getline(file2, line)) {
if (linesInFile.find(line) == linesInFile.end()) {
outputFile2 << line << std::endl; // Записываем уникальную строку
}
}
// Закрываем файлы
outputFile2.close();
file2.close();
std::cout << "all done" << std::endl;
return 0;
}
Сорян, комменты остались частично от предыдущего варианта, мне лень было на них отвлекаться, тем более, что изменения может проследить и йож.
upd3 Представим, что ресурсами мы не ограничены и выполнение должно быть максимально быстрым. Лепим горбатого асинхронщину!!!
В принципе, основное на чем мы можем сэкономить — распараллелить поиск уникальных строк и запись в файлы:
#include <fstream>
#include <string>
#include <unordered_set>
#include <iostream>
#include <future>
#include <vector>
std::unordered_set<std::string> readLines(const std::string& filename) {
std::unordered_set<std::string> lines;
std::ifstream file(filename);
std::string line;
if (!file.is_open()) {
throw std::runtime_error("error opening: " + filename);
}
while (std::getline(file, line)) {
lines.insert(line);
}
return lines;
}
void writeUniqueLines(const std::unordered_set<std::string>& fromSet,
const std::unordered_set<std::string>& toSet,
const std::string& outputFile) {
std::ofstream out(outputFile);
if (!out.is_open()) {
throw std::runtime_error("error opening: " + outputFile);
}
for (const auto& str : fromSet) {
if (toSet.find(str) == toSet.end()) {
out << str << std::endl;
}
}
}
int main() {
try {
// Считываем данные асинхронно
auto future1 = std::async(std::launch::async, readLines, "file1.txt");
auto future2 = std::async(std::launch::async, readLines, "file2.txt");
// Получаем результаты асинхронных задач
auto set1 = future1.get();
auto set2 = future2.get();
// Записываем уникальные строки в файлы асинхронно
auto writeFuture1 = std::async(std::launch::async, writeUniqueLines, set1, set2, "file3.txt");
auto writeFuture2 = std::async(std::launch::async, writeUniqueLines, set2, set1, "file4.txt");
// Ожидаем завершения записи
writeFuture1.get();
writeFuture2.get();
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
return 1;
}
return 0;
}
Вот так, казалось бы, такая простая задачка, и столько подходов в её реализации!