Strona 1 z 2

Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: środa, 14 października 2009, 16:40
przez Pitek
Dziękuję uprzejmie. Jestem początkującym programistą i czase proste żeczy mnie blokują proszę więc o wyrozymiałość.
Mam jeszcze problemy z którymi nie potrafie sobie poradzić.
W funkcji która szuka frazy w tabeli chciałbym aby po znalezieniu szukanego tekstu program przechodził do tej komórki.
Np. Jestem w 20 a ten tekst jest 250 jest on podświetlony ale sam muszę go szukać.
Oraz mam problem z obsługą wyjątków.
Po podaniu nazwy pliku w polu Edit która nie istnieje chciałbym aby wyświetlała mi się informacja którą sam zdefiniuję a nie błąd systemowy.
Proszę o pomoc jeżeli są to żeczy błache to przepraszam ale ja nie daje rady :roll:

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: środa, 14 października 2009, 23:31
przez Witold
pitek3010 napisał(a):Np. Jestem w 20 a ten tekst jest 250 jest on podświetlony ale sam muszę go szukać.


Jeżeli chodzi o wiersz to:
Kod: Zaznacz cały
StringGrid1->TopRow = 250; // Poczytaj w helpie.



Oraz mam problem z obsługą wyjątków.
Po podaniu nazwy pliku w polu Edit która nie istnieje chciałbym aby wyświetlała mi się informacja którą sam zdefiniuję a nie błąd systemowy.


Pole Edit to komponent TEdit, czy coś innego ? Wklej może trochę kodu.

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: czwartek, 15 października 2009, 10:38
przez Cyfrowy Baron
Ja nie bardzo rozumiem pytanie, piszesz jakoś tak nieskładnie.

Co do sprawdzania czy plik istnieje to można posłużyć się funkcją BOOL FileExists(nazwa pliku)

Kod: Zaznacz cały
if(FileExists("nazwa pliku") == true) ShowMessage("Plik istnieje");
else ShowMessage("Brak pliku");

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: piątek, 16 października 2009, 20:32
przez Pitek
Bardzo dziękuję za odpowiedzi, pomogły mi .
Ale nie do końca. I tutaj właśnie postaram sie opisać mój problem bardziej zrozumiale.
Chodzi o wyszukiwanie tekstu w tabeli, mój problem dotyczy tego aby po znalezieniu szukanego tekstu program automatycznie przechodził do komurki ze znalezionym tekstem.
Będę na przyszłośc starał pisać sie bardziej zrozumiale.
Jeżeli komuś nie chce się odpowiadać na moje pytanie niech tego nie robi, chodzi mi o to żeby darować sobie komentarze bardzo o to proszę.

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: piątek, 16 października 2009, 22:48
przez Cyfrowy Baron
Jedyny sposób to przeszukać tabelę komórka po komórce.

Podaje przykład z funkcją wyszukującą:

Kod: Zaznacz cały
void __fastcall Szukaj(String findText, TStringGrid *Tabela)
{
  static int x = 0;
  statit int y = 0;

  LAB_1:

for(int i = x; i < Tabela->ColCount; x++)
{
   for(int j = y; j < Tabela->RowCount; j++)
   {
      String tekst = Tabela->Cells[i][j].Trim();

      if(tekst.LowerCase() == findText.Trim().LowerCase())
      {
        StringGrid1->Col = i;
        StringGrid1->Row = j;
        x = i;
        y = j
       
        return;
      }
   }
}
 
int idx =  Application->MessageBox( ("W tabeli ie odnaleziono tekstu \"" + findText + "\". Czy chcesz przeszukać tabelę od początku?").c_str(),
                                         "Wyszukiwanie", MB_YESNO | MB_ICONQUESTION);
x = 0;
y = 0;

if(idx == ID_YES)  goto LAB_1;
}
//-----------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  String fText = "poszukiwany tekst";
 
  Szukaj(fText, StringGrid1);
}
//------------------------------------------------------------------------

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 10:50
przez polymorphism
Zamiast:
Kod: Zaznacz cały
void __fastcall Szukaj(String findText, TStringGrid *Tabela)
{
   ...

LAB_1:

   ...
   if(idx == ID_YES)  goto LAB_1;
}

zrób:
Kod: Zaznacz cały
void __fastcall Szukaj(String findText, TStringGrid *Tabela)
{
   ...

   do
   {
      ...
   }
   while(idx == ID_YES);
}

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 13:05
przez Cyfrowy Baron
W podanym przeze mnie kodzie, nie da się zastosować rozwiązania podanego przez polymorphism, gdyż komunikat byłby wyświetlany przed każdym rozpoczęciem wyszukiwania, podczas gdy założenie kodu jest takie: szukaj tekstu w komórka rozpoczynając wyszukiwanie od ostatnio zapamiętanej komórki, jeżeli nic nie znajdziesz zadaj pytanie czy wyszukiwać od początku, jeżeli znajdziesz zaznacz komórkę ze znalezionym tekstem i przerwij wyszukiwanie. Nie da się tego tak zrobić z pętlą while.

Kod zawierał błędy podaję działający.

Kod: Zaznacz cały
     void __fastcall Szukaj(String findText, TStringGrid *Tabela)
    {
          static int x = 1;
          static int y = 1;
          static String memText = "";

          if(memText.Trim().LowerCase() != findText.Trim().LowerCase())
          {
            x = 1;
            y = 1;
          }

          LAB_1:

        for(int i = y; i < Tabela->RowCount; i++)
        {
           for(int j = x; j < Tabela->ColCount; j++)
           {
              String tekst = Tabela->Cells[i][j].Trim();
              Tabela->Col = j;
              Tabela->Row = i;
              if(tekst.LowerCase() == findText.Trim().LowerCase())
              {
                Tabela->Col = j;
                Tabela->Row = i;

                if(j < Tabela->ColCount) x = j + 1; else x = 1;
                if(i < Tabela->RowCount) y = i; else y = 1;
                memText = findText;
                return;
              }
           }
           x = 1;
        }

        int idx =  Application->MessageBox( ("W tabeli ie odnaleziono tekstu \"" + findText + "\". Czy chcesz przeszukać tabelę od początku?").c_str(),
                                                 "Wyszukiwanie", MB_YESNO | MB_ICONQUESTION);
        x = 1;
        y = 1;

       if(idx == ID_YES) goto LAB_1;// Szukaj(findText, Tabela);
    }
        //-----------------------------------------------------------------------
        void __fastcall TForm1::Button1Click(TObject *Sender)
        {
          String fText = "poszukiwany tekst";
         
          Szukaj(fText, StringGrid1);
        }
        //------------------------------------------------------------------------


pętli while nie da się tutaj zastosować, gdyż komunikat może być wyświetlany tylko w sytuacji, gdy zostanie przeszukana cała tabela i nie znajdzie już więcej poszukiwanego tekstu.

Zasada działania:

Po zleceniu wyszukania nowego tekstu funkcja Szukaj rozpoczyna wyszukiwanie od pierwszej komórki Tabeli, jeżeli znajdzie tekst zaznacza znalezioną komórkę (by zaznaczenie działało właściwość Options -> goAlvaysShowEditor musi mieć wartość false) i zapamiętuje w zmiennych (x, y) numer komórki, gdy ponownie zlecimy wyszukiwanie tego samego tekstu (może się zdarzyć, że tekst powtarza się w komórkach) funkcja rozpocznie poszukiwanie tekstu od ostatniej zapamiętanej pozycji, a nie od początku. Gdy wyszukiwanie dojdzie do końca Tabeli, funkcja zada pytanie (tylko w tej sytuacji), czy rozpocząć wyszukiwanie od początku, jeżeli TAK, funkcja powróci do do początku poprzez funkcję goto (zamiast goto można użyć wywołania rekurencyjnego, ja jednak lubię posługiwać się goto, chociaż jest potencjalnie niebezpieczna, w tym kodzie jednak sprawdza się w 100%). Jeżeli odpowiedź na pytanie brzmi NIE, funkcja przerywa wyszukiwanie i ustawia się na pierwszą komórkę tabeli. Jeżeli funkcja wykryje, że poszukiwany tekst się zmieni to funkcja rozpoczyna wyszukiwanie od początku.

Przykład z wywołaniem rekurencyjnym:

Kod: Zaznacz cały
//---------------------------------------------------------------------------
void __fastcall Szukaj(String findText, TStringGrid *Tabela)
{
      static int x = 1;
      static int y = 1;
      static String memText = "";

      if(memText.Trim().LowerCase() != findText.Trim().LowerCase())
      {
        x = 1;
        y = 1;
      }

    for(int i = y; i < Tabela->RowCount; i++)
    {
       for(int j = x; j < Tabela->ColCount; j++)
       {
          String tekst = Tabela->Cells[j][i].Trim();
          Tabela->Col = j;
          Tabela->Row = i;
          if(tekst.LowerCase() == findText.Trim().LowerCase())
          {
            Tabela->Col = j;
            Tabela->Row = i;

            if(j < Tabela->ColCount) x = j + 1; else x = 1;
            if(i < Tabela->RowCount) y = i; else y = 1;
            memText = findText;
            return;
          }
       }
       x = 1;
    }

    int idx =  Application->MessageBox( ("W tabeli ie odnaleziono tekstu \"" + findText + "\". Czy chcesz przeszukać tabelę od początku?").c_str(),
                                             "Wyszukiwanie", MB_YESNO | MB_ICONQUESTION);
    x = 1;
    y = 1;

   if(idx == ID_YES) Szukaj(findText, Tabela);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
      String fText = Edit1->Text;

      Szukaj(fText, StringGrid1);
}
//---------------------------------------------------------------------------


Zmieniłem zakres wyszukiwania z 0,0 na 1,1 by ominąć komórki nagłówka. Jeżeli nagłówki mają tylko kolumny, a wiersze nie maja nagłówka to wszędzie tam gdzie y = 1; zmieniasz na y = 0;

Z pętlą while nie będzie działał prawidłowo, sam zdecyduj co wolisz przestarzała i niezalecaną funkcję goto, czy też wywołanie rekurencyjne.

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 13:28
przez polymorphism
W podanym przeze mnie kodzie, nie da się zastosować rozwiązania podanego przez polymorphism, gdyż komunikat byłby wyświetlany przed każdym rozpoczęciem wyszukiwania [...]

Z pętlą while nie będzie działał prawidłowo, sam zdecyduj co wolisz przestarzała i niezalecaną funkcję goto, czy też wywołanie rekurencyjne.

O czym ty piszesz?! Przecież kod z pętlą do...while będzie tym samym, co kod ze skokiem if...goto. Tu chodzi o styl, który w przypadku użycia goto nie jest zalecany. That's all. A jeśli są tam jakieś błędy, to nie jest to wina użycia pętli do...while. Po prostu kod był od początku źle napisany, nie wnikałem w to...

Sposób rekurencyjny jest obłędny 8-)

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 13:56
przez Cyfrowy Baron
Kod był błędnie napisany i nie było to związane z pętlą do...while..., źle go napisałem. Pętla można zastosować, ale nie wiedzę takiej potrzeby dlatego zalecam użyć funkcję goto, która jest co prawda niezalecana, ale tylko ze względu na możliwość wystąpienia błędów wynikających z możliwości zapętlenia się programu. Tak czy inaczej podałem również rozwiązanie zawierające wywołanie rekurencyjne.

Podaję trzy rozwiązania:

z użyciem pętli do...while...
► 

z użyciem funkcji goto:
► 

z użyciem wywołania rekurencyjnego:
► 


I tak się podaje odpowiedzi....

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 17:02
przez Witold
Cyfrowy Baron napisał(a):Jeżeli funkcja wykryje, że poszukiwany tekst się zmieni to funkcja rozpoczyna wyszukiwanie od początku.


Oprócz tekstu może zmienić się jeszcze zmienić TStringGrid na którym szuka się tekstu.

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 17:12
przez Cyfrowy Baron
To prawda. W takiej sytuacji należy zadeklarować zmienne x,y jako globalne i w zdarzeniu OnSetEditText dla StringGrid należy ustawić wartość tych zmiennych na 1 lub 0 zależy od którego wiersza i kolumny chcemy rozpocząć wyszukiwanie.

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 19:11
przez Witold
Można by też pozycja od której trzeba szukać podawać jako argument funkcji, a jako wynik odbierać współrzędne gdzie szukany tekst wystąpił czy -1,-1 gdy go nie znaleziono.

TPoint Szukaj(TStringGrid *SGrid, const AnsiString & tekst, const TPoint & startPoz = TPoint(0,0), bool ZacznijSzukOdNastZaStartPoz = false);

Inspirowane przez : PosEx gdzie podaje parametr Offset (http://4programmers.net/Delphi/PosEx)

i TListView::FindData gdzie jest parametr Inclusive (http://infodelphi.ru/Help/RADStudio/en/ComCtrls.TCustomListView.FindData.html)

Powinno działać…

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: sobota, 17 października 2009, 19:34
przez Cyfrowy Baron
To co proponujesz niepotrzebnie skomplikuje wszystko.
Najlepiej byłoby stworzyć nowy komponent w oparciu TStringGrid i zawrzeć w nim funkcję wyszukiwania.

Twój pomysł mnie zainteresował, ale oczekiwałbym czegoś więcej, czyli kompletnego kodu, a nie tylko nagłówka funkcji - jeżeli nie stanowi to problemu...

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: niedziela, 18 października 2009, 10:59
przez Witold
Cyfrowy Baron napisał(a):To co proponujesz niepotrzebnie skomplikuje wszystko.


Tak, skomplikuje, czy niepotrzebnie - chyba nie, w każdym razie Twoja też nie jest prosta ;) . Znasz jakoś funkcje bibloteczną, która szuka czegoś i zapamiętując pozycje itd, wewnątrz (zmienne statyczne) jak Twoja?

Cyfrowy Baron napisał(a):Najlepiej byłoby stworzyć nowy komponent w oparciu TStringGrid i zawrzeć w nim funkcję wyszukiwania.

Też tak myślę. Szkoda że TStringGrid jej nie ma.

Cyfrowy Baron napisał(a):N
Twój pomysł mnie zainteresował, ale oczekiwałbym czegoś więcej, czyli kompletnego kodu, a nie tylko nagłówka funkcji - jeżeli nie stanowi to problemu...


Właściwie to nie wiem ta funkcja ma dokładnie robić. Więc spróbowałem napisać coś podobnego do Twojej funkcji, bez zmiennych statycznych wewnątrz jej. Zamiast podawać pozycje startowe do szukania można by polegać na właściwościach Col, Row. Nie testowałem funkcji dokładnie, bo to zabiera sporo czasu a mi ta funkcja nie jest potrzebna.

Kod: Zaznacz cały
#include <algorithm>

const int OZNACZA_ZE_NIE_ZNALEZIONO = -1, OD_POCZATKU = -1;
const AnsiString fText = "poszukiwany tekst";
//---------------------------------------------------------------------------
TPoint Szukaj(TStringGrid *SGrid, const AnsiString & szukTxt,
              const TPoint startPoz = TPoint(OD_POCZATKU, OD_POCZATKU),
              bool ZacznijSzukOdNastZaStartPoz = true);
//---------------------------------------------------------------------------
TPoint Szukaj(TStringGrid *SGrid, const AnsiString & szukTxt, const TPoint startPoz,
              bool ZacznijSzukOdNastZaStartPoz)
{
    int start_c = (ZacznijSzukOdNastZaStartPoz) ? startPoz.x+1: startPoz.x;
    start_c = std::max(SGrid->FixedCols, start_c);

    int start_r =  std::max(SGrid->FixedRows, int(startPoz.y));

    for (int r = start_r; r < SGrid->RowCount; ++r)
    {
        for (int c = start_c; c < SGrid->ColCount; ++c)
        {
            const AnsiString TxtZKom = SGrid->Cells[c][r];
            if (AnsiSameText(szukTxt, TxtZKom)) // opcja AnsiSameStr
            {
                SGrid->Col = c ; // opcja
                SGrid->Row = r ; //
                return TPoint(c,r);
            }
        }
        start_c = SGrid->FixedCols; // opcja
    }
    return TPoint(OZNACZA_ZE_NIE_ZNALEZIONO,OZNACZA_ZE_NIE_ZNALEZIONO);
}
//---------------------------------------------------------------------------
TPoint start(OD_POCZATKU,OD_POCZATKU), znaleziono(0,0);
const char * nie_znal_ani_razu = "Tekst \"%s\" nie został odnaleziony. ";
const char * dotarlem_do_konca = "Dotarłem do końca tabeli.";
//---------------------------------------------------------------------------
void __fastcall TForm3::btnSzukajClick(TObject *Sender)
{
    znaleziono = Szukaj(StringGrid1, fText, start);
    if (znaleziono.x != OZNACZA_ZE_NIE_ZNALEZIONO)
    {
        //....
    }
    else
    {
        AnsiString msg;
        if ((start.x == OD_POCZATKU) && (start.y == OD_POCZATKU))
        {
            msg.sprintf(nie_znal_ani_razu, fText.c_str());
        }

        msg += dotarlem_do_konca;

        Application->MessageBox(msg.c_str(), "Info...", MB_ICONINFORMATION);
    }
    start = znaleziono;
}
//---------------------------------------------------------------------------

Re: Wyszukiwanie tekstu w tabeli StringGrid.

Nowy postNapisane: niedziela, 18 października 2009, 12:11
przez Cyfrowy Baron
Oparłem swój kod na funkcjach statycznych, gdyż chciałem zhermetyzować wszystko w jednej funkcji, dzięki temu nie trzeba się martwić zmiennymi globalnymi, które przy rozrośniętej aplikacji mogą się "pogubić", jedna funkcja załatwia całą sprawę.

Co do uwagi:

Właściwie to nie wiem ta funkcja ma dokładnie robić.


Też dokładnie nie wiem, dlatego przyjąłem założenie, ze funkcja ma wyszukiwać zadany tekst w komórkach tabeli, po znalezieniu tekstu przerywa wyszukiwanie i zaznacza komórkę, jednocześnie przestawia się na dalsze przeszukiwanie tabeli od miejsca w którym znaleziono tekst. Jeżeli poszukiwany tekst się zmieni to funkcja przestawia się na przeszukiwanie tabeli od początku. Twoja sugestia, że może się zmienić tekst w tabeli wymusza modyfikację mojej funkcji poprzez zmianę zmiennych statycznych na globalne, gdyż tabela musi poinformować w jakiś sposób funkcję o tym, że zaszły w niej zmiany. Mógłbym pozostać przy zmiennych statycznych, a dodać zmienną globalną typu BOOL, stan której zmieniałby się w zdarzeniu OnSetEditText na true, a w funkcji wyszukującej na false. Gdyby ta zmienna miała wartość true, funkcja zaczynałaby wyszukiwanie od początku, w przypadku znalezienia tekstu wartość tej zmiennej byłaby ustawiana na false, to by jeszcze uprościło wszystko, a zmienne najbardziej istotne pozostałyby zhermetyzowane wewnątrz funkcji.
Hermetyzacja funkcji ma tą zaletę, że to funkcja zapamiętuje wszystkie ustawienia, więc to rozwiązanie jest najbliższe rozwiązanie ze stworzeniem nowego komponentu.
Zbudowanie nowego komponentu w oparciu o klasę TStringGrid z podaną funkcją jest zadaniem prostym, a zaletą jest to, ze w miarę postępów nad programem można by rozbudowywać taki komponent dostosowując go do potrzeb programu, no i wszystkie funkcje byłyby zhermetyzowane w komponencie, więc nic by się nie "pałętało" w klasie formularza.



Nie sprawdzałem Twojego kodu, ale wydaje się dość zagmatwany. Zmienna fText nie powinna chyba być typu const, gdyż będzie zachodzić potrzeba częstej zmiany jej wartości, poprzesz pobranie jej np. z obiektu Edit.