Самый эффективный способ сопоставить два текстовых файла? - PullRequest
2 голосов
/ 14 марта 2020

Допустим, у меня есть два текстовых файла. Текстовый файл «A.txt» содержит имена и их возраст. Текстовый файл "B.txt" содержит названия и их вес. Текстовые файлы имеют различный порядок имен.

//text file "A.txt"
Jason    20
Jack     34
Amanda   15
Einstein 65
Kelvin   47
//text file "B.txt"
Einstein 70
Amanda   55
Jack     99
Kelvin   85
Jason    68

Как наиболее эффективно использовать наименьшее количество операций для чтения и сопоставления этих 2 текстовых файлов и установки их атрибутов в массив объекта класс?

class Person{
    private:
        string name;
        int age;
        int weight;
    public:
        //setter method
}

int main(){
    Person haha[5];
    //code to read files and stores into haha

}

Ответы [ 2 ]

1 голос
/ 14 марта 2020

Вы хотите использовать std::unordered_map.

В целом ваш алгоритм будет выглядеть так:

  1. Строка за строкой читайте первый текстовый файл и вставьте их в std::unordered_map экземпляр. Где ключом карты будет какая-то строка, уникальная для каждого человека, например, его имя. И объект Person является значением. Временная сложность для вставки в std::unordered_map будет O(1).
  2. Строка за строкой читать следующий файл. И попробуйте найти Person в std::unordered_map с тем же именем с std::unordered_map::find. Если это на карте, то это дубликат, иначе вы вставите его в std::unordered_map снова. Временная сложность для std::unordered_map::find составит O(1).

Если вы хотите иметь массив из Person объектов, то вы можете создать его и переместить туда объекты. Но вы также можете выполнять итерацию по std::unordered_map, поэтому его можно использовать вместо массива Person s.

0 голосов
/ 14 марта 2020

Вы можете придумать элегантные решения, если используете современные элементы языка C ++.

Секрет сопоставления вещей заключается в использовании ассоциативных контейнеров, таких как std::map. В этом вы можете сохранить ключ (например, имя) и значение (например, вес) и посмотреть его очень быстро.

И я принял проектное решение: человек может иметь только вес, если у него есть возраст. Это означает, что если файл с весами содержит больше записей, чем файл с возрастами, я буду игнорировать этих дополнительных лиц.

Чтобы добавить элементы в ваш класс Person, я добавил конструктор, который просто копирует значения переменных-членов. Вес необязательный.

И, чтобы показать хороший вывод, я переписал оператор вставки для класса Person.

Оба исходных текстовых файла содержат std::string и integer. Для использования более простого алгоритма чтения я создал прокси-класс для чтения std::pair<std::string, int>.

. Для чтения файла весов мы просто определим карту и затем используем ее конструктор диапазона, чтобы прочитать все значения. через std::istream_iterator в сочетании с определенным классом Proxy для наших пар string-int. Обратите внимание, что конечный итератор будет по умолчанию создан через {}. И мы используем функцию CAD 17 C ++ («дедукция аргументов шаблона класса»), чтобы определить std :: map без аргументов шаблона.

Для сопоставления значений мы используем std::transform, который будет читать файл возрастов, найдите соответствующий вес и добавьте результат в наш вектор персон.

И последнее, но не менее важное, мы показываем результат на дисплее.

Пожалуйста, посмотрите полный рабочий пример :

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <string>
#include <utility>
#include <iterator>
#include <algorithm>

// Define a proxy class, to read a pair from a stream
struct StringIntegerPair : public std::pair<std::string, int> {
    friend std::istream& operator >> (std::istream& is, StringIntegerPair& sip) {
        return is >> sip.first >> sip.second;
    }
};

// Our test class
class Person {
private:
    std::string name{};
    int age{};      // 0 means: not set
    int weight{};   // 0 means: not set
public:
    //setter method

    // Constructor
    Person(const std::string& n, const int a, const int w = 0) : name(n), age(a), weight(w) {}
    // Output
    friend std::ostream& operator << (std::ostream& os, const Person& p) {
        return os << "Name: " << p.name << "\tAge: " << p.age << "\tWeight: " << p.weight;
    }
};

int main() {

    // Open file with weights and check, if it could be opened
    if (std::ifstream weightStream("r:\\b.txt"); weightStream) {

        // Open file with ages and check, if it could be opened
        if (std::ifstream ageStream("r:\\a.txt"); ageStream) {

            // Read all weights into a map
            std::map namesAndWeights(std::istream_iterator<StringIntegerPair>(weightStream), {});

            // Here we will store all persons
            std::vector<Person> persons{};

            // Now read all persons with age, check, if a weight is existing and add it to the vector
            std::transform(std::istream_iterator<StringIntegerPair>(ageStream), {}, std::back_inserter(persons),
                [&namesAndWeights](const std::pair<std::string, int>& na) { return Person(na.first, na.second, namesAndWeights[na.first]); });

            // Show result to user
            std::copy(persons.begin(), persons.end(), std::ostream_iterator<Person>(std::cout, "\n"));
        }
        else {
            std::cerr << "\n*** Error: Could not open file with ages\n";
        }
    }
    else {
        std::cerr << "\n*** Error: Could not open file with weights\n";
    }
    return 0;
}

...