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


Автор: avatara 14.11.2007, 23:01
Помогите написать код подсчета количества одинаковых строк и определения начальной позиции данной строки в списке. 
Есть ListBox, в котором находятся данные типа:
АА
АА
АА
АА
АБ
АБ
АБ
АВ
АГ
АГ
АД
ЕЕ
ЕЕ
КР
КР
МО
МО
ПС

Необходимо в EditBox вывести данные таким образом:
АА - позиция 0, кол-во 4; АБ - позиция 4, кол-во 3; АВ - позиция 7, кол-во 1; АГ - позиция 8, кол-во 1; АД - позиция 10, кол-во 1; ЕЕ -  позиция 11, кол-во 2 .......     и т.д.

Данные уже отсортированы, затруднение вызывает написание кода подсчета.

Спасибо.

Автор: avatara 15.11.2007, 11:31
Это надо было, чтобы узнать начальный индекс данной строки и количество.
На самом деле все немного сложнее. В проекте есть ListCtrl, в котором 2 столбца - Серия и Номер
например
ЖХ    222222
ЖЦ    222223
ЖЦ    333333
ЖЦ    222221
ЖЦ    444440
ЖЦ    344444
ЖЦ    322222
ЖЦ    311111
ЖЧ    332222
ЖЧ    355555
ЖЧ    111111
ЖЧ    355555
ЖЧ    322221
ЖЧ    355555
ЖЧ    355554
ЖЧ    355553
ЖЧ    366666
ЖЧ    366667
ЖЧ    366669
ЖЧ    366668
ЖЧ    300000
ЖЧ    363910
ХЩ    295363
в первом столбце данные уже отсортированы по алфавиту, и нужно отсортировать данные во втором столбце соответственно внутри СЕРИИ
Вот две функции

Код

void CNewMarkDlg::OnBnClickedButton2()
{
    // Подсчет количества и начала одинаковых записей
    int kol;
    kol = m_list.GetItemCount();
    CString Seria, num, Sstart;
    int count = 1;
    int pos = 0;
    for (int i = 1; i < kol; ++i)
    {
        if (m_list.GetItemText(i, 0) == m_list.GetItemText(pos, 0))
            count++;
        else
        {
            //Seria = m_list.GetItemText(pos, 0);
            //num.Format("%i", count);
            //Sstart.Format("%i", pos);
            
            PuzirikSort(pos, count); // если вместо рos и count указывать числа, то все нормально работает
            //m_str = m_str + Seria + ' ' + "поз - " + Sstart + ' ' + "кол-во - " + num;
            pos = i;
            count = 1;
        }
    }
    UpdateData(FALSE);
}
// Сортировка методом пузырька
void CNewMarkDlg::PuzirikSort(int StartIndex, int Size)
{
    int *ListAr;
    ListAr = new int[Size];
    for (int i = 0; i < Size; i++)
    {
        ListAr[i] = StrToInt(m_list.GetItemText(i, 1));
    }
    int temp;
    for (int pass = 1; pass < Size; pass++)
    {
        for (int j = 0; j < Size - 1; j++)
        {
            if (ListAr[j] > ListAr[j+1])
            {
                temp = ListAr[j];
                ListAr[j] = ListAr[j + 1];
                ListAr[j + 1] = temp;
            }
        }
    }
    CString strmass, element;
    for (i = 0; i < Size; i++)
    {
        strmass.Format("%i", ListAr[i]);
        m_list.SetItemText(StartIndex + i, 1, strmass);
    }
    delete []ListAr;
}

Сама функция сортировки PuzirikSort работает правильно, если указывать числовые значения в ее параметрах, а если использовать переменные pos и count то часть данных просто изчезает, а вместо них появляются дубликаты имеющихся.
Посмотрите, в чем проблемы. На всякий случай прикрепляю файл с проектом. В папке ДЕБАГ есть текстовые файлики с данными для загрузки.

Автор: Ln78 15.11.2007, 13:23
avatara, я не понял, что тебе нужно: решить задачу с сортировкой самому, или просто, чтобы было всё отсортировано. Если второе - лучше воспользоваться готовыми алгоритмами, типа sort.  Или другими контейнерами, в которых такая сортировка происходит автоматически, на этапе заполнения, например, map. Например, в твоей функции OnBnClickedButton1 можно написать что-то вроде 
Код

   CString itogo;
   std::map<CString,int> mapStr;
   int i = 0;
   while (fOpen.ReadString(str))
  {
        mapStr[str] = i;
  }
  std::map<CString,int>::iterator it;
  for(it=mapStr.begin(); it!=mapStr.end(); ++it)
  {
      stritem = ((*it).first).Mid(0,2);
      strsubitem = ((*it).first).Mid(3,9);
      m_list.InsertItem(i, stritem);
      m_list.SetItemText(i, 1, strsubitem);
      i++;
   }
   UpdateData(FALSE);

Здесь я, правда, дважды поленился: если используется не одна CString, а какая-нибудь структура, то для неё нужно определить операцию "меньше", чтобы использовать map. И лучше, по соображениям эффективности, использовать дополнительный класс - указатель на структуру, чтобы уменьшить число операций копирования. Если это интересно, я могу позже (когда будет свободное время) написать этот фрагмент с учётом этих замечаний.

Автор: avatara 15.11.2007, 14:03
Да, нужно чтобы все было отсортировано - СЕРИИ по алфавиту и НОМЕРА по возрастанию внутри  серии. Серии уже отсортированы и нужно сортировать только номера в серии.
Сортировать можно как угодно, хоть собстренную делать сортировку, хоть использовать стандартные метоты. 
Я не очень силен в программировании и с контейнером map еще невстречался. 
Делаю проект  MFC application в Visual Studio 2003.net подскажите Какие инклюды сделать чтобы Ваш пример заработал.

Автор: Ln78 15.11.2007, 15:04
Нужно добавить
Код

#include <map>

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

Автор: avatara 15.11.2007, 15:53
Сделал, все заработало, за исключением одного НО. 
Серии стали сортироваться в другом порядке. 
В программе серии должны отсортированы согласно украинского алфавита

а б в г ґ д е є ж з і ї й к л м н о п р с т у ф х ц ч ш щ ю я ь

Поэтому в исходном файле, который загружается в программу, такая сортировка уже сделана по сериям согласно украинскому алфавиту

В данном случае при загрузке файла с данными происходит пересортировка серий.  И в результате серии с начальными буквами Є,  Ї,  І   становятся в самом начале списка, т.е. нарушается порядок следования букв в алфавите.

Автор: zkv 15.11.2007, 16:14
Цитата(avatara @  15.11.2007,  15:53 Найти цитируемый пост)
В данном случае при загрузке файла с данными происходит пересортировка серий.  И в результате серии с начальными буквами Є,  Ї,  І   становятся в самом начале списка, т.е. нарушается порядок следования букв в алфавите.

Цитата(Ln78 @  15.11.2007,  13:23 Найти цитируемый пост)
для неё нужно определить операцию "меньше", чтобы использовать map

Автор: avatara 15.11.2007, 16:59
Цитата(Ln78 @ 15.11.2007,  13:23)
Здесь я, правда, дважды поленился: если используется не одна CString, а какая-нибудь структура, то для неё нужно определить операцию "меньше", чтобы использовать map. И лучше, по соображениям эффективности, использовать дополнительный класс - указатель на структуру, чтобы уменьшить число операций копирования. Если это интересно, я могу позже (когда будет свободное время) написать этот фрагмент с учётом этих замечаний.

Был бы очень рад, заодно и поучился бы правильно делать такое.

Автор: Ln78 15.11.2007, 18:07
avatara, я, честно говоря, не силён в украинском, увидел какие-то чудные буковки, но не придал этому значения. Возможно, для украинского пройдёт такой вариант
Код


_locale_t locale;

class MyCmp
{
public:
   bool operator() (const CString& S1, const CString& S2) const
   {
      int r = _stricmp_l(LPCTSTR(S1),LPCTSTR(S2),locale);
      return r < 0;
   }
};
void CNewMarkDlg::OnBnClickedButton1()
{
    // TODO: Add your control notification handler code here
    CString m_FileName;

    m_FileName = "";
    CFileDialog OpenDialog(true, "txt", m_FileName, OFN_FILEMUSTEXIST | OFN_HIDEREADONLY, "Text Files (*.txt)|*.txt||");

   locale = _create_locale(LC_ALL, "Ukrainian");

    if (OpenDialog.DoModal() == IDOK)
    {
        CStdioFile fOpen(OpenDialog.GetPathName(), CFile::modeRead);
        CString str, stritem, strsubitem;
        CString itogo;
      std::map<CString,int,MyCmp> mapStr;
        int i = 0;
        while (fOpen.ReadString(str))
        {
         mapStr[str] = i;
        }
      std::map<CString,int,MyCmp>::iterator it;
      for(it=mapStr.begin(); it!=mapStr.end(); ++it)
      {
         stritem = ((*it).first).Mid(0,2);
         strsubitem = ((*it).first).Mid(3,9);
         m_list.InsertItem(i, stritem);
         m_list.SetItemText(i, 1, strsubitem);
         i++;
      }
        UpdateData(FALSE);
        fOpen.Close();
        itogo.Format("%i", i);
        m_stat.SetWindowText(itogo);
    }
    
}

Я как-то и не в состоянии проверить, правильно ли после этого работает. А сама идея с определением операции меньше здесь продемонстрирована, просто здесь - сравнение с учётом украинской (мне так кажется, ни разу до этого не делал smile ) локали. Можно, если в примере где-то уже есть реализация сравнения украинских букв, написать свою реализацию оператора bool operator() .

Автор: avatara 16.11.2007, 09:38
По MSDN нашел какие надо сделать инклюды (в stdafx.h)

Код

#include <locale>
#include <iostream>
#include <tchar.h>


Выдает ошибки
error C2146: syntax error : missing ';' before identifier 'locale'
error C2501: '_locale_t' : missing storage-class or type specifiers
error C2501: 'locale' : missing storage-class or type specifiers
error C3861: '_create_locale': identifier not found, even with argument-dependent lookup

Несилен я в STL, простите.


 

Автор: Ln78 16.11.2007, 11:04
avatara, это мой недосмотр, забыл дописать, что нужно добавить
Код

#include <locale.h>
#include <map>


Автор: avatara 16.11.2007, 11:42
Те же самые ошибки выдает.

Автор: Ln78 16.11.2007, 11:56
Странно, а хэлп что выдаёт? У меня сейчас нет под рукой 2003-й, делал в 2005-й, подключал в NewMarkDlg.cpp, никаких ошибок не выдаётся, в справке указано:
Цитата

Requirements
Routine                               Required header                   Compatibility  
_create_locale                    <locale.h>                           Windows 95, Windows 98, Windows 98 Second Edition, Windows Millennium Edition, Windows NT 4.0, Windows 2000, Windows XP Home Edition, Windows XP Professional, Windows Server 2003

попробуй сделать Rebuild

Автор: avatara 16.11.2007, 12:23
Rebuild не помог, сегодня дома поставлю 2005-ю Студию и попробую.
Спасибо еще раз.

Автор: Ln78 17.11.2007, 11:53
avatara, вот версия файла, чтобы работал в 2003-й VS

Автор: avatara 17.11.2007, 16:08
Спасибо огромное, все заработало. 
Еще раз спасибо. 

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