Litet problem med krypteringsprogram i C++

Permalänk

Litet problem med krypteringsprogram i C++

Hej.
Jag håller på att skriva ett enkelt krypteringsprogram i C++, men efter lite ändringar i koden för att göra den mer effektiv så fungerar inte programmet som jag vill. Här kommer koden:

#include <iostream> #include <algorithm> #include <string> #include <fstream> using namespace std; using std::cout; using std::cin; string get_line(); void write_line(char* sentence); void encrypt(char* sentence); void decrypt(char* sentence); string get_line() { ifstream in; string text; cout << "Note: Everything in file must be on one line!\n"; in.open("unprocessed.txt"); getline(in, text); while (in) { getline(in, text); } return text; } void write_line(char* sentence) { ofstream out; out.open ("processed.txt"); out << sentence <<endl; out.close(); return; } void encrypt(char* sentence) { for( int i=0; sentence[i] != '\0'; ++i ) ++sentence[i]; reverse (sentence, sentence + strlen (sentence)); } void decrypt(char* sentence) { reverse (sentence, sentence + strlen (sentence)); for( ; *sentence != '\0'; ++sentence ) --(*sentence); } int main() { int answer; char sentence[2000]; string sentence2; string fromfile; do { cin.ignore(); cout << "What would you like to do?\n"; cout << "-------------------------------------------\n"; cout << "1. Encrypt text in a file\n"; cout << "2. Encrypt a sentence within this program\n"; cout << "-------------------------------------------\n"; cout << "3. Decrypt text in a file\n"; cout << "4. Decrypt a sentence within this program\n"; cout << "-------------------------------------------\n"; cout << "5. Exit.\n"; cin >> answer; switch(answer) { case '1': fromfile = get_line(); strcpy_s(sentence, fromfile.c_str()); encrypt(sentence); write_line(sentence); system ("pause"); break; case '2': cin.ignore(); cout << "Write your sentence here: \n"; getline (cin, sentence2); strcpy_s(sentence, sentence2.c_str()); encrypt(sentence); cout << "The encrypted version of your sentence is: " << sentence << "\n"; system ("pause"); break; case '3': fromfile = get_line(); strcpy_s(sentence, fromfile.c_str()); decrypt(sentence); write_line(sentence); system ("pause"); break; case '4': cin.ignore(); cout << "Write your sentence here: \n"; getline (cin, sentence2); strcpy_s(sentence, sentence2.c_str()); decrypt(sentence); cout << "The decrypted version of your sentence is: " << sentence << "\n"; system ("pause"); break; default: cout << "Input not accepted\n"; } } while(answer != '5'); return 0; }

Jag har två frågor till er kunniga här på forumet:

1. Programmet startar fint och allt ser ut så som jag vill att det ska göra i menyerna. När jag väljer ett av alternativen beskrivna i main-funktionen och trycker retur så får jag - oavsett vilken siffra jag trycker - Input not accepted. Vad jag vill att att programmet endast ska ge det meddelandet om man trycker in fel siffra (alltså inte 1, 2, 3, 4 eller 5). Vad har jag gjort för fel?

2. Jag har länge haft problemet att funktionen get_line() bara har returnerat den sista raden i txt-filen.

string get_line() { ifstream in; string text; cout << "Note: Everything in file must be on one line!\n"; in.open("unprocessed.txt"); getline(in, text); while (in) { getline(in, text); } return text; }

Om jag till exempel skriver in "Hej. Vad gör du?" så får den med hela, men om jag skriver
"Hej.
Vad gör du?" så skickar den bara tillbaka "Vad gör du?" till krypteringsfunktionen. Jag tror jag vet vad som orsakar problemet, men inte hur jag ska lösa det. Är det inte så att funktionen läser in första raden i "text" och upptäcker sedan att filen inte är slut, så den läser in ytterligare en rad samtidigt som den förra försvinner. Tillslut är det bara den sista raden kvar i "text" och först då returnerar funktionen "text" (eftersom loopen avslutas när texten i filen är slut). Så finns det över huvud taget något sätt att få in många rader i 1 string (har sökt som en dåre på Google efter detta, men förgäves)eller måste jag ta till någon slags array-lösning?

Uppskattar verkligen all hjälp jag kan få, men försök att hålla saker och ting enkla eftersom jag är en nybörjare.
/Calle

Permalänk
Hedersmedlem
Skrivet av t0t4l str3:

När jag väljer ett av alternativen beskrivna i main-funktionen och trycker retur så får jag - oavsett vilken siffra jag trycker - Input not accepted. Vad jag vill att att programmet endast ska ge det meddelandet om man trycker in fel siffra (alltså inte 1, 2, 3, 4 eller 5). Vad har jag gjort för fel?

Det är för att du läser in användarens val i en int (som alltså får värden som 1, 2, 3, 4 eller 5) men sedan försöker matcha detta mot tecken ('1', '2', '3', '4' och '5' (som har värdena 49, 50, 51, 52 och 53)). Skriv istället (utan fnuttar):

case 1: //...... break; case 2: //..... break;

Permalänk

Jaha, så de där ('=fnuttar?) små tecknen gör att datorn inte längre ser siffrorna inuti som tal, utan bara som tecken? Ska testa att ta bort dem genast.

Angående den andra frågan så har jag tänkt lite och jag tycker det borde vara möjligt att få in de olika raderna i en string array och sedan returnera hela arrayen. Jag har provat det innan men när sedan arrayen skickades till krypteringsfunktionen så gick något fel. Tror det var att for-satsen inte kunde ta emot en array eller något. Dessutom har jag aldrig varit bra på for-satser så jag brydde inte mig om det då. Jag har också för mig att det blev lite komplikationer vid skapandet av arrayen eftersom jag inte visste hur många rader textfilen innehöll. Men finns det något som heter dynamisk array kanske kan lösa just det problemet?
Ska googla lite och försöka implementera lite av det jag hittar. Återkommer sedan.
Edit: Det kanske föresten heter Vector i C++.
/Calle

Permalänk
Medlem
Skrivet av t0t4l str3:

2. Jag har länge haft problemet att funktionen get_line() bara har returnerat den sista raden i txt-filen.

string get_line() { ifstream in; string text; cout << "Note: Everything in file must be on one line!\n"; in.open("unprocessed.txt"); getline(in, text); while (in) { getline(in, text); } return text; }

du måste gör while satsen så den kör tills du kommer till slutet av filen
sen är jag osäker på hur getline fungerar men du kanske måste spara om strängen då getline troligt viss bara skriver över det som stod där innan

while (!in.eof) { String temp; getline(in, temp); text = text+temp; }

typ något sådant

edit: glöm inte att stänga filen också
in.close();

Visa signatur

orka

Permalänk
Citat:

sen är jag osäker på hur getline fungerar men du kanske måste spara om strängen då getline troligt viss bara skriver över det som stod där innan

Ja så är det nog. Den returnerar bara den sista raden eftersom den skriver över hela tiden. Kan detta lösas med någon form av dynamisk array/vector?
/Calle

Permalänk
Medlem
Skrivet av t0t4l str3:

Jaha, så de där ('=fnuttar?) små tecknen gör att datorn inte längre ser siffrorna inuti som tal, utan bara som tecken? Ska testa att ta bort dem genast.

Japp, 5 är en int, '5' är en char och "5" är en sträng. En char görs automatiskt om till en int om så krävs, men då enligt ASCII. Så '5' blir då 53 som Elgot säger.

Skrivet av t0t4l str3:

Angående den andra frågan så har jag tänkt lite och jag tycker det borde vara möjligt att få in de olika raderna i en string array och sedan returnera hela arrayen. Jag har provat det innan men när sedan arrayen skickades till krypteringsfunktionen så gick något fel. Tror det var att for-satsen inte kunde ta emot en array eller något. Dessutom har jag aldrig varit bra på for-satser så jag brydde inte mig om det då. Jag har också för mig att det blev lite komplikationer vid skapandet av arrayen eftersom jag inte visste hur många rader textfilen innehöll. Men finns det något som heter dynamisk array kanske kan lösa just det problemet?
Ska googla lite och försöka implementera lite av det jag hittar. Återkommer sedan.
Edit: Det kanske föresten heter Vector i C++.
/Calle

Om du ska kryptera en fil så vill du förmodligen läsa in hela filen binärt. Om du läser in med t.ex. getline så tappas t.ex. nyrads-tecken bort, så du får då lägga in dem själv. Om du läser in binärt så får du med allting. Exempel på hur du läser in en hel fil binärt finns nästan längst ner på denna sida.

Du borde förresten inte hålla på och blanda string och char* som du gör i din kod. Det finns ju ingenting i din kod som kräver char*, så du skulle kunna skriva om allting så att du bara använder string istället.

Skrivet av t0t4l str3:

Ja så är det nog. Den returnerar bara den sista raden eftersom den skriver över hela tiden. Kan detta lösas med någon form av dynamisk array/vector?
/Calle

Jo, du skulle ju kunna använda en vector. Men en string är ju annars också dynamisk, så du kan bara slå ihop strängarna som miffo visade.

Permalänk
Medlem
Skrivet av t0t4l str3:

Ja så är det nog. Den returnerar bara den sista raden eftersom den skriver över hela tiden. Kan detta lösas med någon form av dynamisk array/vector?
/Calle

se det jag skrev inom code tagen

bara onödigt komplicerat att hålla på med arrayer eller vectorer, för string är i princip en dynamisc array av chars ändå

Visa signatur

orka

Permalänk
Skrivet av perost:

Japp, 5 är en int, '5' är en char och "5" är en sträng. En char görs automatiskt om till en int om så krävs, men då enligt ASCII. Så '5' blir då 53 som Elgot säger.

Om du ska kryptera en fil så vill du förmodligen läsa in hela filen binärt. Om du läser in med t.ex. getline så tappas t.ex. nyrads-tecken bort, så du får då lägga in dem själv. Om du läser in binärt så får du med allting. Exempel på hur du läser in en hel fil binärt finns nästan längst ner på denna sida.

Du borde förresten inte hålla på och blanda string och char* som du gör i din kod. Det finns ju ingenting i din kod som kräver char*, så du skulle kunna skriva om allting så att du bara använder string istället.

Jo, du skulle ju kunna använda en vector. Men en string är ju annars också dynamisk, så du kan bara slå ihop strängarna som miffo visade.

Okej. Om jag läser in hela filen binärt, kommer jag då kunna öppna filen i krypterat läge och se hur texten inuti ser ut? Annars låter det som en bra idé.

Det här med string och char har jag inte så bra koll på, som du ser, men jag ska försöka förklara hur jag har tänkt.
int_main får tillbaka stringen text från getline(). Text ska sedan skickas till funktionen encrypt och det är for-satsen i encrypt som ställer till det för mig. Den tar bara emot konstanta chars (tydligen) det är därför som jag använder:

strcpy_s(sentence, fromfile.c_str());

Den hoppas jag gör om string till char så att encrypt funktionen kan kryptera text. Om det går att lösa på annat sätt så är jag mottaglig för det.

Jag håller just nu på med miffos lösning, men det blir rödmarkerat under while(!in.eof) med förklaringen "A pointer to a bound function may only be used to call the function" Ska se om jag kan lösa det.
Edit: getline() ser ut så här nu:

string get_line() { char *filename = "unprocessed.txt"; ifstream in(filename); string text; while (!in.eof) { string temp; getline(in, temp); text = text+temp; } return text;

Permalänk
Medlem
Citat:

void encrypt(char* sentence)
{
for( int i=0; sentence[i] != '\0'; ++i ) ++sentence[i];
reverse (sentence, sentence + strlen (sentence));
}

bara så jag har koll din cryptering är att vända strängen baklänges och lägga till 1 på alla tecken?

jag tror du bara kan skicka den som än sträng
och använda iteratorn för att stega igenom strängen

void encrypt(String* sentence) { for(itr=sentence.begin;itr < sentence.end; itr++) *itr++; }

sedan kan du nog använda begin och end i reverse så här

void encrypt(String* sentence) { for(itr=sentence.begin;itr < sentence.end; itr++) *itr++; reverse(sentence.begin, sentence.end); }

tror det borde funka

Visa signatur

orka

Permalänk

Ja, du har rätt. Min kryptering fungerar så att man flyttar bokstäverna ett snäpp och vänder på hela strängen.
Ska testa det du skrev senare idag, men hur fungerar när jag ska dekryptera? Är det bara att byta ut "*itr++;" mot "*itr--;"?
/Calle

Permalänk
Medlem
Skrivet av t0t4l str3:

Jag håller just nu på med miffos lösning, men det blir rödmarkerat under while(!in.eof) med förklaringen "A pointer to a bound function may only be used to call the function" Ska se om jag kan lösa det.
Edit: getline() ser ut så här nu:

string get_line() { char *filename = "unprocessed.txt"; ifstream in(filename); string text; while (!in.eof) { string temp; getline(in, temp); text = text+temp; } return text;

det ska viss vara !in.eof() tror jag

sitter inte vid en kompilator själv just nu så läser mer vad man kan göra och det kan bli att jag läser fel

edit: igen kom ihåg att köra in.close() innan du lämnar funktionen

Visa signatur

orka

Permalänk
Medlem
Skrivet av t0t4l str3:

Ja, du har rätt. Min kryptering fungerar så att man flyttar bokstäverna ett snäpp och vänder på hela strängen.
Ska testa det du skrev senare idag, men hur fungerar när jag ska dekryptera? Är det bara att byta ut "*itr++;" mot "*itr--;"?
/Calle

helt riktigt.

Visa signatur

orka

Permalänk

Okej, jag har provat nu.
Det största problemet verkar vara att det inte går att konvertera från std::string till std::string *, men är inte asterisken bara en pekare? Hur kan då en omvandling behöva göras? Edit: Hade glömt att sätta asterisker vid deklarationerna.
Sedan klagar den på att jag inte har deklarerat itr (Ska den vara en integer?) på ett par ställen och att "*sentence must have class type" Vad jag vet så håller jag inte på med objektorienterad programmering än (det är väl då man använder klasser?).
Här kommer i alla fall koden som den ser ut nu:

#include <iostream> #include <algorithm> #include <string> #include <fstream> using namespace std; using std::cout; using std::cin; string get_line(); void write_line(string *sentence); void encrypt(string *sentence); void decrypt(string *sentence); string get_line() { char *filename = "unprocessed.txt"; ifstream in(filename); string text; while (!in.eof()) { string temp; getline(in, temp); text = text+temp; } in.close(); return text; } void write_line(string *sentence) { ofstream out; out.open ("processed.txt"); out << sentence <<endl; out.close(); return; } void encrypt(string *sentence) { int *itr = 0; for(itr=sentence.begin;itr < sentence.end; itr++) *itr++; reverse(sentence.begin, sentence.end); } void decrypt(string *sentence) { for(itr=sentence.begin;itr < sentence.end; itr++) *itr--; reverse(sentence.begin, sentence.end); } int main() { int answer; string *sentence; string sentence2; string fromfile; do { cin.ignore(); cout << "What would you like to do?\n"; cout << "-------------------------------------------\n"; cout << "1. Encrypt text in a file\n"; cout << "2. Encrypt a sentence within this program\n"; cout << "-------------------------------------------\n"; cout << "3. Decrypt text in a file\n"; cout << "4. Decrypt a sentence within this program\n"; cout << "-------------------------------------------\n"; cout << "5. Exit.\n"; cin >> answer; switch(answer) { case 1: fromfile = get_line(); encrypt(sentence); write_line(sentence); system ("pause"); break; case 2: cin.ignore(); cout << "Write your sentence here: \n"; getline (cin, sentence2); encrypt(sentence); cout << "The encrypted version of your sentence is: " << sentence << "\n"; system ("pause"); break; case 3: fromfile = get_line(); decrypt(sentence); write_line(sentence); system ("pause"); break; case 4: cin.ignore(); cout << "Write your sentence here: \n"; getline (cin, sentence2); decrypt(sentence); cout << "The decrypted version of your sentence is: " << sentence << "\n"; system ("pause"); break; default: cout << "Input not accepted\n"; } } while(answer != '5'); return 0; }

Permalänk
Medlem

bara för att underlätta och att jag personligen inte orkar tänka pekar just nu
så är det lättare att köre att den retunerar den modifierad strängen

string encrypt(string sentence) { int *itr = 0; for(itr=sentence.begin;itr < sentence.end; itr++) *itr++; reverse(sentence.begin, sentence.end); return sentence; }

Visa signatur

orka

Permalänk
Medlem
Skrivet av t0t4l str3:

Sedan klagar den på att jag inte har deklarerat itr (Ska den vara en integer?) på ett par ställen och att "*sentence must have class type" Vad jag vet så håller jag inte på med objektorienterad programmering än (det är väl då man använder klasser?).

string är en klass

Skrivet av miffo:

bara för att underlätta och att jag personligen inte orkar tänka pekar just nu
så är det lättare att köre att den retunerar den modifierad strängen

string encrypt(string sentence) { int *itr = 0; for(itr=sentence.begin;itr < sentence.end; itr++) *itr++; reverse(sentence.begin, sentence.end); return sentence; }

Bra tänkt, men för att inte förvirra t0t4l str3 alltför mycket så är här en fungerande (hoppas jag ) implementation:

string encrypt(string sentence) { string::iterator itr; for(itr = sentence.begin(); itr != sentence.end(); ++itr) { *itr++; } reverse(sentence.begin(), sentence.end()); return sentence; }

Vill man att encrypt ska fungera som den ursprungliga funktionen, dvs. operera på inparametern direkt, så är det bara att göra om den till "void encrypt(string &sentence)" och låta bli att returnera något.

Det är ju också rätt så enkelt att göra inkrementeringen och reverse på samma gång, dvs. ta första tecknet från en sträng, öka det, och stoppa tecknet sist i en annan sträng. Då slipper man gå igenom strängen två gånger, så det blir snabbare. Men det lämnar jag som övning till t0t4l str3

Permalänk
Medlem

oj missa helt det där med iter frågan. sitter på jobbet och skriver lite snabbt i smyg h0h0
så är nog ganskas slarvig

Visa signatur

orka

Permalänk

Okej. Det verkar som om koden är nästan bra nu. Jag får inget fel i VS 2010 men det blir istället fel när jag kör programmet.
Kan ladda upp en bild om ni vill se.
Det står så här:
"Debug assertion failed!
Expression: String iterator not decrementable"
Trycker jag ignorera så får jag ytterligare en och sedan krashar programmet. Jag som tycker att det borde fungera vid det här laget.
Edit: Konstigt, det fungerade när jag gjorde en releaseversion. =P Nu klarar den många rader också. Det enda problemet nu är att det är något med encryptfunktionen. Den ökar inte bokstäverna som innan, den bara vänder texten baklänges. Det måste vara något som fattas i den.

string encrypt(string sentence) { string::iterator itr; for(itr = sentence.begin(); itr != sentence.end(); ++itr) { *itr++; } reverse(sentence.begin(), sentence.end()); return sentence; }

/Calle

Permalänk
Medlem

[QUOTE=t0t4l str3;10311902]

string encrypt(string sentence) { string::iterator itr; for(itr = sentence.begin(); itr != sentence.end(); ++itr) { *itr++; } reverse(sentence.begin(), sentence.end()); return sentence; }

/Calle[/QUOTE]

(*itr)++; ska det vara. Vad som sker annars är:

  1. Hämta värdet under iteratorn.

  2. Inkrementera iteratorn så den pekar på nästa element.

Paranteserna gör att inkrementeringen istället sker på det värde som hämtats.

Visa signatur

Vill du ha svar? Citera mig gärna.

Permalänk

Tack. Nu fungerar programmet precis så som jag vill att det ska göra!
Jag vill passa på att tacka alla vänliga själar som har tagit sin tid att hjälpa mig idag. Jag uppskattar det verkligen.
Om någon vill se koden till programmet i färdig version så är det bara att hojta till, så lägger jag upp det här. Ni har även gett mig idéer till vad jag kan göra för att förbättra programmet. Några av dessa är:
-Binär kryptering.
-Användardeffinerat lösenord, givetvis med kryptering.
-Förbättrad krypteringsalgoritm(stega 5 tecken fram istället för ett till att börja med).
Tack och hej.
/Calle