Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > замена вложенных циклов


Автор: kachu 5.6.2013, 15:53
здравствуйте. 
Нужно было написать программу, а задание следующее: Имеется телефонный справочник в текстовом формате. В каждой строке файла указаны фамилия, имя, адрес, телефон, дата рождения. Поля разделены пробелом. Записать в новый текстовый файл все данные об однофамильцах. Имена всех файлов задаются в командной строке.

программу написал, но очевидно, что первый (внешний) цикл while сработает 1 (один) раз. Подскажите как это можно исправить?

Код

#include <iostream>
#include <fstream>
#include <string>

using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 3) 
    {
        printf("Вы забыли ввести имена файлов\n");
        exit(1);
    }
    else
    {
        char *line1 = new char[255];
        char *line2 = new char[255];
        char *full1 = new char[1000];
        char *full2 = new char[1000];
        int flag = 0;

        ifstream fin(argv[1]);
        ofstream fout(argv[2]);

        while (fin)
        {
            fin >> line1;
            fin.getline(full1,1000);

            
            while (fin)
            {
                
                fin >> line2;
                fin.getline(full2,1000);

                if (strcmp(line1,line2)==0)
                {
                    if (flag == 0) 
                    {
                        fout << line1 << full1 << "\n";
                        flag = 1;
                    }
                    fout << line2 << full2 << "\n";
                }
            }
        }
    }
}

Автор: mes 5.6.2013, 16:26
Цитата(kachu @  5.6.2013,  14:53 Найти цитируемый пост)
 что первый (внешний) цикл while сработает 1 (один) раз

в обоих циклах один и тот же идентификатор условия окончания цикла (fin)

Автор: kachu 5.6.2013, 16:31
mes,  я понимаю, но не знаю как можно не создавая копию этого потока исправить это, т.е. не создавая  ifstream fin2(argv[1]) например.

Автор: mes 5.6.2013, 18:23
Цитата(kachu @  5.6.2013,  15:31 Найти цитируемый пост)
 но не знаю как можно не создавая копию этого потока исправить это, 

сформулировать для начала на словах алгоритм действий..


другими словами: определите, что должен делать Ваш внешний цикл, а что внутренний.. 

Автор: volatile 5.6.2013, 18:29
Код

fin.seekg (0, std::ios::beg);


Автор: kachu 5.6.2013, 18:45
mes, внешний цикл - перебор строк файла, а внутренний - аналогично перебирает строки того же файла и сравнивает строку, полученную во внешнем цикле со всеми остальными строками.
т.е. аналог 
Код

for (int i = 0; i < countoflines; i++)
    for (int j = 0; j < countoflines; j++)
// тут берется i-я строка и сравнивается со всеми j-ми
// но все эти строки в одном файле



volatile, я думал вернуться в начало файла, но опять же не понял как это реализовать.

Автор: volatile 5.6.2013, 19:07
kachu, я бы отсортировал, (если память конечно позволяет) потом одним циклом вывел всех однофамильцев.
Двойным циклом, как вы хотите будут одни и теже выводицца по нескольку раз, если однофамильцев больше двух.



Автор: kachu 5.6.2013, 19:11
volatile, спасибо за идею, буду пробовать

Автор: mes 5.6.2013, 19:37
Цитата(kachu @  5.6.2013,  17:45 Найти цитируемый пост)
 внешний цикл - перебор строк файла, а внутренний - аналогично перебирает строки того же файла и сравнивает строку, полученную во внешнем цикле со всеми остальными строками.

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

Добавлено через 4 минуты и 30 секунд
но предложение от volatile реализуется легче и эффективнее (для файлов "нормальной" длины)  smile 

Автор: volatile 5.6.2013, 20:02
Можно вообще просто сделать.
(нехорошо поступаю конечно, что готовый ответ даю smile) 
Код

...
#include <map>
int main(int argc, char *argv[])
{
   // ...
    ifstream fin(argv[1]);
    ofstream fout(argv[2]);

    std::string name;
    typedef std::map <std::string, int> map_t;
    map_t mp;
    
    while (fin >> name)
    {
        ++ mp [name];
        fin.ignore (9999, '\n');
    }
    for (map_t::iterator i = mp.begin(); i != mp.end(); ++ i)
       if (i->second > 1)
          fout << i->first << std::endl;
} 

Автор: mes 5.6.2013, 20:08
volatile, в простой мапе теряется информация о всех однофамильцах... 

Цитата(kachu @  5.6.2013,  14:53 Найти цитируемый пост)
Записать в новый текстовый файл все данные об однофамильцах.


Добавлено через 1 минуту и 23 секунды
плюс боюсь трудновато будет тс с ходу понять...

Автор: volatile 5.6.2013, 20:16
Цитата(mes @  5.6.2013,  20:08 Найти цитируемый пост)
Записать в новый текстовый файл все данные об однофамильцах.

ах, сорри, не обратил внимания...
ну дописывать уже не буду. пусть ТС сам дальше...  smile 

Автор: kachu 6.6.2013, 14:13
volatile, 
mes, 
спасибо Вам. 

по поводу map: нельзя мне использовать, т.к. разрешено использовать только то что даётся преподавателем.

проблему с повторным открытием потока в цикле решил, разобрался всё-таки с seekg. 
использовал такую конструкцию:
Код

while ()
{
i = fin.tellg(); // запоминаем позицию
 while() {}    //работаем

fin.clear();    // сбрасываем флаг
fin.seekg(pos,fin.beg); //возвращаемся к сохраненной позиции
}


всё работает, но замечание 
Цитата(volatile @  5.6.2013,  19:07 Найти цитируемый пост)
Двойным циклом, как вы хотите будут одни и теже выводицца по нескольку раз, если однофамильцев больше двух.

конечно справедливо, но в данном случае можно сдать работу и так.

Автор: volatile 7.6.2013, 08:16
Цитата(kachu @  6.6.2013,  14:13 Найти цитируемый пост)
while ()
{
i = fin.tellg(); // запоминаем позицию
 while() {}    //работаем

fin.clear();    // сбрасываем флаг
fin.seekg(pos,fin.beg); //возвращаемся к сохраненной позиции
}

kachu, Молодец! Правильно все сделали.
Мне лишь не нравицца алгоритм двойного прохода.
мало того что он не правильный, он еще и очень долгий. сложность O(N^2), что при больших N выльецца в  очень долгий процесс.

Цитата(kachu @  5.6.2013,  15:53 Найти цитируемый пост)
Имеется телефонный справочник в текстовом формате.

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


Автор: kachu 8.6.2013, 10:38
volatile, спасибо за похвалу

по поводу сложности вы видимо правы, из всех заданий это одно помечено "*", и единственное вызвало затруднения

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)