• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

C++ Obiektowo - odc. 2: Porównanie zmiennych

+1 głos
110 wizyt
pytanie zadane 27 listopada 2021 w C i C++ przez KubaM Nowicjusz (130 p.)

Cześć,

natrafiłem na problem w 2 odcinku Obiektowego C++.

Program w instrukcji warunkowej if() nie jest w stanie porównać dwóch zmiennych typu std::string przy użyciu operatora ==. 

Jedna ze zmiennych jest wczytywana z pliku .txt, a druga wczytywana za pomocą std::cin>> . 

Zawartość zmiennych wyświetlonych na ekran z użyciem std::cout<< jest taka sama (np. "c" oraz "c").

Nie znalazłem działającego rozwiązania tego problemu, próbowałem użyć funkcji .compare() - bezskutecznie.

Co może być istotne - korzystam z Xcode w systemie macOS.

void Pytanie::sprawdz()
{
   if(odpowiedz==poprawna)
   {
      punkt=1;
   }
   else punkt=0;
}

Proste pytanie - co jest nie tak?

1 odpowiedź

+1 głos
odpowiedź 27 listopada 2021 przez Wiciorny Ekspert (219,270 p.)
edycja 27 listopada 2021 przez Wiciorny

Bo stringi nie porównuje się operatorem "==" porównania, ten operator sprawdza referencje- adres w pamięci, czyli tam gdzie jest zapisany obiekt, a nie to czy jest on równy - jako literał. 
Stąd dwa różne obiekty jako string mające ten sam literał, mają inny adres w pamięci

do porównania stringów służy "compare np"

https://www.cplusplus.com/reference/string/string/compare/

They compare equal -> jeśli są równe zwrócone zostanie 0 ... dlaczego u Ciebie nie zadziała? Bo takie porównanie jak niżej zwróci zero, a if (0) -> oznacza false

if(odpowiedz.compare(poprawna))

i o ile kod jest poprawny to zadziała, w wyżej opisany sposób dla równych znaków zwraca wartości całkowite opisane w dokumentacji tutaj dla równych 0,  dlatego wrzuć jak wyglądają twoje zmienne :), i wrzuć resztę kodu programu 

więc należy to zapisać, zapisując w ten sposób jeśli są równe-> będą zwracać 0 więc warunek 0 == 0 jest prawdziwy 

a samo if(0) jest fałszem, bo 0 -> jako boolean to fałsz 

if(odpowiedz.compare(poprawna) ==0 )

Pamiętaj że wczytując znaki- nie wczytujesz stringa, a CHAR stąd to nie jest to samo 

komentarz 27 listopada 2021 przez KubaM Nowicjusz (130 p.)

Dziękuję za odpowiedź. Pisałem kod bez użycia using namespace std; reszta była kopią kodu z odcinka, dla testu skopiowałem cały kod z odcinka do kompilatora jednak nadal nie działało (to również mnie ciekawi, dlaczego Panu Mirosławowi program zadziałał...).

#include <iostream>

using namespace std;

class Pytanie
{
   public:

   string tresc;
   string a,b,c,d;
   int nr_pytania;
   string poprawna;
   string odpowiedz;
   int punkt;

   void wczytaj(); //wczytuje pytania z pliku
   void zadaj(); //pokazuje pytanie, czyta odpowiedz
   void sprawdz(); //sprawdza poprawnosc odpowiedzi
};
#include <iostream>
#include "pytanie.hpp"
#include <fstream>
#include <cstdlib>

using namespace std;

void Pytanie::wczytaj()
{
   fstream plik;
   plik.open("quiz.txt",ios::in);

   if(plik.good()==false)
   {
      cout<<"Nie udalo sie otworzyc pliku!";
      exit(0);
   }

   int nr_linii=(nr_pytania-1)*6+1;
   int aktualny_nr=1;
   string linia;

   while(getline(plik,linia))
   {
    
      if(aktualny_nr==nr_linii) tresc=linia;
      if(aktualny_nr==nr_linii+1) a=linia;
      if(aktualny_nr==nr_linii+2) b=linia;
      if(aktualny_nr==nr_linii+3) c=linia;
      if(aktualny_nr==nr_linii+4) d=linia;
      if(aktualny_nr==nr_linii+5) poprawna=linia;
      aktualny_nr++;
   }

   plik.close();

}

void Pytanie::zadaj()
{

   cout<<endl<<tresc<<endl;
   cout<<a<<endl;
   cout<<b<<endl;
   cout<<c<<endl;
   cout<<d<<endl;
   cout<<"------------------------"<<endl;
   cout<<endl<<"Odpowiedz: ";
   cin>>odpowiedz; //getline(cin, odpowiedz); - to też nie działa
}

void Pytanie::sprawdz()
{
    if(odpowiedz==poprawna) //odpowiedz.compare(poprawna)==0)- też nie działa
    {
      punkt=1;
   }
   else punkt=0;
}
#include <iostream>
#include "pytanie.hpp"

using namespace std;

int main()
{

   Pytanie p[5];
   int suma=0;
   for(int i=0; i<=4; i++)
   {
      p[i].nr_pytania=i+1;
      p[i].wczytaj();
      p[i].zadaj();
      p[i].sprawdz();
      suma+=p[i].punkt;
   }

   cout<<"KONIEC QUIZU! PUNKTY = "<<suma;

   return 0;
}

Dla rozjaśnienia - który z tych znaków będzie CHARem, wczytany z pliku .txt, czy ten sam wpisany przy użyciu std::cin , czy może oba? funkcja .compare() porówna ze sobą CHARy?

komentarz 27 listopada 2021 przez Wiciorny Ekspert (219,270 p.)

oba, jeśli wczytane bedą po 1 literce, zależy od zmiennej jeśli zmienna jest string to compare wystarczy w zupełności, ale pamiętaj o tym jakie wartości zwraca compare i ze dla równych ciągów jest to 0- a taka wartość dla instrukcji warunkowej oznacza nieprawdę stąd musisz to porównac

Compare porównuje stringi u Ciebie, bo zmienne

 string poprawna; string odpowiedz;

jeśli używasz jakiś funkcji czytaj ich dokumentacje, tam masz wszystko napisane.  

Compares the value of the string object (or a substring) to the sequence of characters specified by its arguments.
 

komentarz 27 listopada 2021 przez KubaM Nowicjusz (130 p.)

W takim razie, dlaczego w powyższym kodzie funkcja użyta w ten sposób:  if(odpowiedz.compare(poprawna)==0) nie działa? tj. nie przyznaje punktu

Jakim cudem Panu Mirosławowi program zadziałał?

komentarz 27 listopada 2021 przez Wiciorny Ekspert (219,270 p.)
edycja 28 listopada 2021 przez Wiciorny

w takim razie problem będzie leżeć w twoim pliku, to jakiego jest formatu może nie jest utf-8 sprawdzałeś co kryję się w zmiennych podczas wykonywania?

Skoro cin>>odpowiedz;  u Ciebie nie działa, to oznacza, że zmienna ta nie ma wartości stąd instrukcja nie bedzie działac, jaki błąd zwraca program? 

Sprawdź jakie masz importy plik pytanie nie jest hpp, a h 
 

#include "pytanie.h"
a u Ciebie jest 
#include "pytanie.hpp"

https://stackoverflow.com/questions/5171502/c-vs-cc-vs-cpp-vs-hpp-vs-h-vs-cxx
Czy sprawdzałeś w ogóle zakres widoczności zmiennych i funkcjonalość dołączonego pliku ?  Oraz to czy odpowiedni masz plik tekstowy 

2
komentarz 28 listopada 2021 przez j23 Mędrzec (173,020 p.)

@Wiciorny, 

Bo stringi nie porównuje się operatorem "==" porównania, ten operator sprawdza referencje- adres w pamięci, czyli tam gdzie jest zapisany obiekt, a nie to czy jest on równy - jako literał. 

Zaraz, zaraz. Piszesz o porównaniu obiektów std::string? Jeśli tak, to to nie jest prawda. To nie Java. Operator == porównuje zawartość obiektów, a nie referencje/adresy. Wykorzystuje do tego wspomnianą std::string::compare.

@KubaM, sprawdź znaczniki końca linii w pliku. W Windowsie powinny być \r\n, Linuksie - \n, Mac - \r.

komentarz 28 listopada 2021 przez KubaM Nowicjusz (130 p.)

@Wiciorny, Program nie zwraca żadnego błędu, porównywane zmienne widzi jako != stąd wykonuje się warunek else punkt=0; przez co mimo wpisanej z klawiatury prawidłowej odpowiedzi (zgodnej z tą w pliku .txt) nie zlicza zdobytych punktów.

Sprawdziłem kodowanie tekstu, w pliku .txt oraz w kompilatorze mam UTF-8.

Program Xcode dla plików nagłówkowych w C++ domyślnie dodaje rozszerzenie .hpp, analizowałem już wcześniej ten wątek na stackoverlfow i z tego co zrozumiałem, aby pliki były czytelne, współcześnie używa się właśnie takich rozszerzeń dla C++.

komentarz 28 listopada 2021 przez KubaM Nowicjusz (130 p.)

@j23, udało się, problemem był znak końca linii. Kombinowałem wcześniej, jednak tylko ze znakiem \n - bo ten znałem, nie wiedziałem że na macOS znak końca linii to \r - cenna lekcja.

Stworzyłem dodatkowego stringa, który dodaje do odpowiedzi wpisanej z klawiatury \r i program działa jak należy :)

Podejrzewam, że nie jest to optymalne rozwiązanie, poszukam funkcji, która w jakiś sposób to usprawni. W każdym razie dzięki. 

komentarz 28 listopada 2021 przez j23 Mędrzec (173,020 p.)
Zamiast kombinować z jakimiś dodatkowym stringiem przekonwertuj plik w jakimś edytorze na właściwy format.
komentarz 29 listopada 2021 przez KubaM Nowicjusz (130 p.)
Właściwy, to znaczy jaki ?
1
komentarz 29 listopada 2021 przez j23 Mędrzec (173,020 p.)
Właściwy, czyli taki, w którym znaki końca linii są poprawne dla systemu, na którym kompilujesz program.

Notepad++ ma opcję zmiany znaków końca linii.
komentarz 30 listopada 2021 przez KubaM Nowicjusz (130 p.)
Falstart, program jednak nie działa poprawnie, prawidłowo odczytuje 4 pierwsze odpowiedzi, a ostatnią zapisaną w ostatniej linii tekstu w pliku odczytuje błędnie.

Dodatkowo, cofnąłem się do kodu z odcinka 7. podstawowego kursu C++, gdzie pisaliśmy ten quiz nie obiektowo, wtedy program działał prawidłowo, dla rozszerzeń pliku .txt oraz .rtf.

Natomiast aktualnie tamten kod również nie działa, więc jest to zastanawiające, musiałem po drodze coś namącić - nie jestem w stanie doszukać się błędu.

Sprawdzałem kodowanie tekstu - plik UTF-8, kompilator (Xcode) UTF-8, okazało się, że w Xcode można również zmieniać "Line Endings" znaki końca linii na: Unix/MacOS, Classic MacOS oraz Windows, próbowałem wszystkich 3 wersji, jednak program nadal nie działa.

Proszę o podpowiedzi, gdzie mogę szukać problemu, program odczytywał poprawnie odpowiedzi z klawiatury i porównywał z pobranymi z pliku, teraz nie jest to możliwe...
komentarz 30 listopada 2021 przez j23 Mędrzec (173,020 p.)

a ostatnią zapisaną w ostatniej linii tekstu w pliku odczytuje błędnie.

jeśli plik był robiony w notatniku windowsowym, sprawdź, czy ostatnia linia zawiera znak końca linii - w Windowsie \r\n to de facto komenda drukarki nakazująca powrót wózka z głowicą na początek i przewinięcie papieru o jedną linię w dół, dlatego ostatnia linia w plikach windowsowych nie ma EOL-a.

komentarz 1 grudnia 2021 przez KubaM Nowicjusz (130 p.)

Masz rację, ostatnie wyrażenie zawiera znak \r\n.

Wciąż nie mogę pojąć, dlaczego kilka tygodni temu wszystko działało, a teraz jest z tym taki problem.

Przypomniałem sobie, że w międzyczasie robiłem update systemu do BigSur 11.6.1 - może to?

Zaktualizowałem również Xcode do najnowszej wersji - to akurat nic nie zmienia w tej kwestii.

Dziwne, że funkcja zmiany znaku końca linii w Xcode nie sprawdza się w tym przypadku.

Aktualnie poradziłem sobie w ten sposób:

if(odpowiedz[0]==poprawna[i][0])
        {
            cout <<"Poprawna odpowiedź! Zdobywasz punkt!"<<endl;
            punkty++;
        }
        else
        {cout<<"Źle! Nie zdobywasz punktu. Poprawna odpowiedź: "<<poprawna[i]<<endl;}

Czyli porównuje ze sobą pierwsze znaki w string'u.

Pytanie, czy jest to najbardziej optymalne rozwiązanie?

Notepad'a ++ nie ma na MacOS, próbowałem użyć programów dos2mac - ale mimo wpisywania komend w terminalu zgodnie z instrukcją nie chciało mi to zadziałać. 

komentarz 2 grudnia 2021 przez j23 Mędrzec (173,020 p.)

Pytanie, czy jest to najbardziej optymalne rozwiązanie?

Od biedy może być. Zastanawia mnie, co tam jeszcze jest w tym stringu. Sprawdzałeś?

Notepad'a ++ nie ma na MacOS

W zasadzie każdy edytor programistyczny powinien mieć taką opcję.

robiłem update systemu do BigSur 11.6.1 - może to?

Ciekawe. Z tego, co wyczytałem, \r jest znakiem końca linii do wersji 9, później jest UNIX-like, czyli \n. Może przekonwertuj plik do \n i zobacz, czy będzie działać bez poprawek w kodzie.

Podobne pytania

0 głosów
3 odpowiedzi 216 wizyt
pytanie zadane 28 grudnia 2018 w PHP przez PWD Nowicjusz (160 p.)
0 głosów
1 odpowiedź 135 wizyt

87,976 zapytań

136,557 odpowiedzi

304,510 komentarzy

58,337 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Sklep oferujący ćwiczenia JavaScript, PHP, rozmowy rekrutacyjne dla programistów i inne materiały

Oto dwie polecane książki warte uwagi. Pełną listę znajdziesz tutaj.

...