Skocz do zawartości

Zarchiwizowany

Ten temat jest archiwizowany i nie można dodawać nowych odpowiedzi.

Sergi

[C++ Builder 6] szyfrowanie

Polecane posty

Męczę się nad napisaniem programu zaliczeniowego z szyfrowania - program ma mieć przyjazny interfejs użytkownika zapisywać i otwierać pliki z tekstem do obróbki no i oczywiście szyfrować (tylko tekst, bez cyfr, znaku przystankowych etc.).

Robiłem już wszystkie trzy (ale nigdy jednego, który potrafi to wszystko :tongue: ) i udało mi się coś takiego wytworzyć (piszę w C++ Builder 6):

http://www.maedur.unl.pl/szyfrowanie.rar

Program kompiluje się i robi prawie wszystko co trzeba, tylko wysypuje się przy próbie zaszyfrowania jakiegokolwiek tekstu. Próbowałem już różnych rzeczy, ale wciąż wywala ten sam błąd:

error.png

Będę wdzięczny za jakąkolwiek pomoc (no, może poza postami w stylu 'google nie boli' bo już szukałem).

Link do komentarza
Udostępnij na innych stronach

Że wskaźnik nie pokazuje na miejsce gdzie jest to, co chcę? Dla pewności zrobiłem sobie ShowMessage(klucz) i pokazuje dobrze. Poszedłem krok dalej i dodałem klucz do znaku ASCII. Też jest dobrze. Wnioskuję, że diabeł tkwi w sposobie w jaki chcę podmieniać znaki w Edit.

Link do komentarza
Udostępnij na innych stronach

Ano coś jest nie tak z wartością wskaźnika. Żeby było nam łatwiej rozmawiać to wrzucę interesujący kawałek kodu:

void __fastcall TForm1::Button3Click(TObject *Sender)
{
int klucz = StrToInt(Edit2->Text);
Edit1->Text=(cezar(Edit1->Text, klucz);
}

Po pierwsze jest tutaj błąd składniowy : (cezar(Edit1->Text, klucz). Brakuje zamykającego nawiasu.

Po drugie co dokładnie robi cezar() ? Pewnie zwraca char *, ale skąd bierze na to pamięć. Alokuje, czy podmienia zawartość oryginalnego bufora. Coś mi mówi, że nie można zrobić takiego manewru Edit->Text = Edit->Text.

Link do komentarza
Udostępnij na innych stronach

Wstawka prywatno-subiektywna: Jak Borland to tylko Delphi! :)

Poważniej: Mormegil Ci już tu ładnie pomaga, ja tylko zarekomenduje używanie czegoś takiego jak debugger. Bez problemu i w moment znalazłbyś instrukcję, która powoduje błędy i wiedział na którym wierszu się skupić.

To IMO najlepsze co można zrobić programiście i jedyny powód (prócz okazjonalnie grania w coś) dla którego przełączam się czasem z Linuksa na Windowsa*

Obsługa debuggera jest prosta - powinieneś w internecie wygooglać jakiś poradnik, praktycznie każdego Windowsowego programu tego rodzaju używa się tak samo. Zresztą w Delphi działa identycznie, więc poradnik dla Delphi będzie ważny też dla C++ Buildera.

*GDB jest ogólnie super, ale traktuje kontenery STL jak zwykłe klasy, więc za Chiny Ludowe nie podejrzysz ich zawartości. Więc jak piszę kod opierający się w dużej części na STLu, to z bólem serca muszę robić reboot. (ja wiem, że istnieją jakieś nieoficjalne poprawki, ale się z nimi jeszcze nie bawiłem. Jak kiedyś znajdę czas to spróbuję, może coś pomoże)

Link do komentarza
Udostępnij na innych stronach

Nawias zaginął w akcji.

A konstrukcja

Edit1->Text=Edit2->Text;

jest dozwolona.

Na wszelki wypadek sprawdziłem jeszcze taką konstrukcję:

zmienna=cezar(a, b);
Edit1->Text=zmienna;

Ale wywala ten sam błąd.

@Alton - nigdy czegoś takiego nie używałem (mam jakąś awersję, nigdy nie rozumiałem bełkotu który wypluwa kompilator po błędzie) i jak podświetla się coś innego niż linijka którą właśnie napisałem, to wpadam w popłoch.

Link do komentarza
Udostępnij na innych stronach

Ale to właśnie nie to. Debugger pozwala na wykonywanie kodu linijka po linijce, czy sprawdzania w dowolnym momencie wartości wszystkich zmiennych. Więc jak go używasz, to widzisz (bardzo czytelnie, wierz mi, w produktach Borlanda debuggery są naprawdę niezłe) w której instrukcji się wywala i jakie były wartości zmiennych w tym momencie. Przy błędach typu "Acces Violation" zwykle zauważasz wtedy jakiegoś NULLa, o którym zapomniałeś. Albo coś podobnie głupiego a podchwytliwego ;)

Link do komentarza
Udostępnij na innych stronach

Winna jest funkcja cezar:

AnsiString cezar(AnsiString wyraz, int klucz)
{
  for (int i=0;i<wyraz.Length();i++)
  {
   if (czylitera(wyraz[i])==true)
   {
    wyraz[i]=wyraz[i]+klucz;
   }
  }
  return wyraz;
}

Otóż najwyraźniej pojedyncze znaki w obiekcie AnsiString indeksuje się od 1 do Length() + 1. Czyli trzeba albo przesunąć pętle o 1, albo

AnsiString cezar(AnsiString wyraz, int klucz)
{
  char * temp = wyraz.c_str();
  for (int i=0;i<wyraz.Length();i++)
  {
   if (czylitera(temp[i])==true)
   {
    temp[i]=temp[i]+klucz;
   }
  }
  return wyraz;
}

Dodam jeszcze, że literki mogą zamienić się w nie literki: ziom + 1 = {ipn. Taki efekt chyba jest nie pożądany.

Link do komentarza
Udostępnij na innych stronach

:huh: Wczoraj dodałem zmienną temp, ale to nie pomogło. Przyznam że trochę się w tym gubię, char * temp = wyraz.c_str(); - to wskaźnik na jeden ze znaków wyrazu?

A nad algorytmem zabezpieczającym przed 'wyjeżdżaniem' właśnie myślę :wink:

Dzięki za pomoc.

Link do komentarza
Udostępnij na innych stronach

char * temp = wyraz.c_str();

AnsiString to klasa. Obiekty tego typu przechowują napis w formie tablicy char[Length + 1]. Metoda c_str() zwraca wskaźnik na tą tablicę znaków. Konkretniej to jest adres pierwszego znaku.

Operator [] klasy AnsiString ma pewien narzut, choćby testowanie indeksu. Użycie temp ma celu uniknięcie tego narzutu, ale oczywiście nie chroni Cię przed "wyjechaniem" poza zakres tablicy.

EDIT:

Długość tablicy to ilość znaków + 1, potrzebne jest jeszcze miejsce na końcowe zero. Także ostatecznie powinno być Length + 1.

Link do komentarza
Udostępnij na innych stronach

Im bardziej staram się to ogarnąć tym mniej rozumiem :huh:

Zacząłem dopisywać instrukcje pilnujące żeby znaki nie wychodziły poza litery i nie dość że to nie działa (wyjeżdżają, a zabezpieczenie przed szyfrowanie innych znaków nie pozwala ich już odszyfrować) to w pewnym momencie małe litery w ogóle przestały się szyfrować. Po drodze wyłapałem że przesuwam o nieprawidłową ilość znaków i niedokładnie sprawdzam (notorycznie nie uznawał a i A za litery). Doprowadziłem do czegoś takiego i już kompletnie nie mam pomysłu co się dzieje:

AnsiString cezar(AnsiString wyraz, int klucz)
{
  char * temp = wyraz.c_str();
  for (int i=0;i<wyraz.Length()+1;i++)
  {
   if (czylitera(temp[i])==true)
   {
   if (((temp[i]+klucz)<65) && (temp[i]>64 && temp[i]<91))     //zabezpieczenie przed wyjsciem poza litery dla wielkich liter
   temp[i]=temp[i]+26;

   if (((temp[i]+klucz)>90) && (temp[i]>64 && temp[i]<91))
   temp[i]=temp[i]-26;

   if (((temp[i]+klucz)>122) && (temp[i]>96 && temp[i]<123))     //zabezpieczenie przed wyjsciem poza litery dla małych liter
   temp[i]=temp[i]-26;

   if (((temp[i]+klucz)<97) && (temp[i]>96 && temp[i]<123))
   temp[i]=temp[i]+26;


   temp[i]=temp[i]+klucz;                                   //własciwe szyfrowanie

   }
  }
  return wyraz;
}

Link do komentarza
Udostępnij na innych stronach

Moim zdaniem dziwne by było, gdyby nie wyskoczył poza litery przy takim rozwiązaniu, no ale zacznijmy od tego dlaczego małe litery nie są szyfrowane.

Dla małych liter są wykonywane dwie komendy:

   if (((temp[i]+klucz)>122) && (temp[i]>96 && temp[i]<123))     //zabezpieczenie przed wyjsciem poza litery dla małych liter
   temp[i]=temp[i]-26;

oraz

   temp[i]=temp[i]+klucz;                                   //własciwe szyfrowanie

Czyli najpierw odejmujemy, a potem dodajemy to samo, czyli siłą rzeczy musimy wyjść na zero. Duże litery są za to szyfrowane podwójnie.

Mój pomysł na rozwiązanie tego problemu jest taki:

- definiujemy nową tablicę charów o długości 52 (tyle ile jest wszystkich liter)

- w każdym polu tej tablicy wpisujemy inną literę (uzyskujemy w ten sposób odwzorowanie różnowartościowe zbioru {0,1,2,...,51} na zbiór liter.

- szyfrowanie będzie polegało na znalezieniu litery w tablicy. Powiedzmy, że dana litera znajduje się na miejscu 'i' miejscu w tablicy, tą literę zastępujemy literą, która znajduje się na miejscu 'i+x mod 52"

- deszyfrowanie analogicznie, tylko odejmujemy 'x'

Link do komentarza
Udostępnij na innych stronach

Czyli najpierw odejmujemy, a potem dodajemy to samo, czyli siłą rzeczy musimy wyjść na zero. Duże litery są za to szyfrowane podwójnie.

Nie szyfruję zawsze kluczem 26 przy którym nic by się nie działo. Specjalnie usunąłem to zabezpieczenie i nadal się nie szyfrują. Wielkie litery przechodzą przez taki sam proces, jak małe.

Link do komentarza
Udostępnij na innych stronach

Daj więc ten kod, w którym nie ma tego +26 i -26. Jeśli kod jest taki sam, tylko, że liczba się zmienia, to w mojej uwadze również zmieni się tylko liczba i tyle.

W kodzie, który podałeś wcześniej duże litery przechodzą przez inny proces niż małe (przeanalizuj kod i weź pod uwagę to, co napisałem w swoim ostatnim poście, tzn. w przypadku dużych liter dwukrotnie była dodawana wartość 26, a w przypadku małych raz była dodawana, a raz odejmowana).

Link do komentarza
Udostępnij na innych stronach

No to już wiem, czemu nie są szyfrowane małe litery:

bool czylitera(char znak)

{

if(znak>64 && znak<91)

return true;

else return false;



if(znak>96 && znak<123)

return true;

else return false;

}

Czy drugi if będzie w ogóle sprawdzany?

A jeśli chodzi o deszyfrację, to błąd jest w zaznaczonym miejscu

AnsiString odcezar(AnsiString wyraz, int klucz)            //odszyfrowywanie

{

  char * temp = wyraz.c_str();

  for (int i=0;i<wyraz.Length()+1;i++)

  {

  if (czylitera(temp[i])==true)                            //czy ten warunek jest spełniony, dla znaków, które już nie są literami?
   {

   temp[i]=temp[i]-klucz;                                   //własciwe szyfrowanie

   }

  }

  return wyraz;

}

Po poprawieniu powyższych rzeczy program oczywiście nadal nie będzie poprawnie działał. Tzn. szyfrowanie będzie ok, ale z odszyfrowaniem może być problem, bo jak mamy rozpoznać, czy dany znak był przed zaszyfrowaniem literą, czy może był znakiem przestankowym, nawiasem, czy jeszcze czymś innym? Dlatego też sądzę, że w przypadku szyfrowania litery powinny przechodzić na litery.

Odejmowanie, czy dodawanie stałej wartości (np. 26 w przypadku poprzedniej wersji) nic nie daje, bo jeśli klucz będzie inny niż ta stała, to i tak możemy wyskoczyć poza litery (sprawdź co się dzieje dla skrajnych liter).

Link do komentarza
Udostępnij na innych stronach

Właśnie po to ta zasada została dodana - jeśli litera z kluczem wyjadą poza zakres, to mają zacząć od początku, prawda?

Więc musimy się cofnąć o cały zestaw znaków, czyli 26 pozycji, stąd to dodawanie i odejmowanie.

Robiłem już taki program w konsolowej wersji i zabezpieczenie w ten sposób działało.

Link do komentarza
Udostępnij na innych stronach

No ok, skoro uważasz, że to zabezpieczenie działa, to może i masz rację. Mogłem się mylić, kod sprawdzałem tylko metodą wzrokową. Choć to zabezpieczenie nie wydaje się być w porządku dla ujemnych kluczy, bo w skrajnym przypadku 26 zostanie dodane dwa razy (np. dla litery "G" i klucza -25).

Małe litery tak jak pisałem nie są szyfrowane, bo nie są uznawane za litery (nie jest nawet sprawdzane, czy są literami, bo do tego if nie dojdzie).

Link do komentarza
Udostępnij na innych stronach



  • Kto przegląda   0 użytkowników

    • Brak zalogowanych użytkowników przeglądających tę stronę.
×
×
  • Utwórz nowe...