CYFROWY BARON • PROGRAMOWANIE • Zobacz wątek - Zapis białych znaków do pliku

Zapis białych znaków do pliku

dział ogólny

Zapis białych znaków do pliku

Nowy postprzez nvdante » środa, 16 marca 2011, 17:36

Witam

Mam prosty programik, który na zapisać do pliku zawartość pól Edit.
Chodzi jednak o to, aby dane były zapisane z białymi znakami, a dokładniej, aby tabulator nie był interpretowany jako kilka spacji tylko właśnie tabulator.
Jak mogę to zrobić?
Do zapisu używam funkcji:
KOD cpp:     UKRYJ  
      ofstream outfile("raport.txt", ios::app);
      if (!outfile) return;
      outfile << Edit1->Text.c_str() << "       " << Edit2->Text.c_str() << endl;
      outfile.close();
Liberae sunt nostrae cogitationes
Avatar użytkownika
nvdante
Intelektryk
Intelektryk
 
Posty: 188
Dołączył(a): piątek, 12 czerwca 2009, 16:58
Lokalizacja: Dom
Podziękował : 8
Otrzymał podziękowań: 0
System operacyjny: Windows 7
Kompilator: VS 2015
Gadu Gadu: 0
    Windows XPFirefox

Re: Zapis białych znaków do pliku

Nowy postprzez Cyfrowy Baron » środa, 16 marca 2011, 18:15

Tabulator - \t

KOD cpp:     UKRYJ  
 ofstream outfile("raport.txt", ios::app);
 if (!outfile) return;
 outfile << Edit1->Text.c_str() << "\t" << Edit2->Text.c_str() << endl;
 outfile.close();


Powinieneś zawsze podawać bezwzględną ścieżkę dostępu do pliku, czyli np:

KOD cpp:     UKRYJ  
ofstream outfile("C:\\raport.txt", ios::app);


Lub względną, ale względem czegoś, np. względem programu:

KOD cpp:     UKRYJ  
String fileName = ExtractFilePath( ParamStr(0) ) + "raport.txt";
ofstream outfile(fileName.c_str(), ios::app);


ParamStr(0) zawsze zwraca ścieżkę dostępu do programu niezależnie od tego gdzie ten program się znajduje. Dokładniej to ParamStr( numer argumentu linii komend ) zwraca argument przekazany do programu z linii komend, lub np. poprzez skrót, ale argument 0 zawsze jest ścieżką dostępu do programu.
ExtractFilePath( ścieżką dostępu do pliku ) ekstrahuje ze ścieżki dostępu do pliku ścieżkę do katalogu w którym ten plik się znajduje.
Załóżmy, że uruchamiamy nasz program z lokalizacji: C:\Program Files\Cyfbar\cyfbar.exe

ParamStr(0) zwróci C:\Program Files\Cyfbar\cyfbar.exe
KOD cpp:     UKRYJ  
String result = ParamStr(0);


ExtractFilePath( ParamStr(0) )
zwróci C:\Program Files\Cyfbar\
KOD cpp:     UKRYJ  
String result = ExtractFilePath( ParamStr(0) );


inny przykład:
KOD cpp:     UKRYJ  
String fileName = "c:\\Program Files\\Adobe\\Reader 10.0\\Reader\\AcroRd32.exe";
String result = ExtractFilePath( fileName );
/* result otrzyma jako wynik c:\\Program Files\\Adobe\\Reader 10.0\\Reader\\


Inna podobna funkcja to ExtractFileDir działa identycznie jak ExtractFilePath, lecz zwraca wynik bez ukośnika na końcu, czyli ze ścieżki C:\Program Files\Cyfbar\cyfbar.exe zwróci: C:\Program Files\Cyfbar

Zamiast ParamStr(0) można użyć Application->ExeName.

W pewnych sytuacjach program może zgubić taką nieokreśloną jak w Twoim przykładzie ścieżkę. Na pewno stanie się tak po użyciu przez użytkownika okna dialogowego TOpenDialog lub TSaveDialog i wybraniu w nim ścieżki dostępu z lokalizacji innej niż lokalizacja programu.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Zapis białych znaków do pliku

Nowy postprzez nvdante » środa, 16 marca 2011, 18:44

Faktycznie działa dobrze, podmieniłem to co mówiłeś i ścieżka podaje się automatycznie.
Chciałbym jeszcze podpytać o wyłapywanie błędów:
KOD cpp:     UKRYJ  
try
{
if(Key == VK_RETURN)
{
if (Edit9->Text.IsEmpty())
        ShowMessage("Proszę poda właściwe dane!");
        else
        Edit10->Text = (Edit1->Text * Edit9->Text) + (Edit3->Text * Edit9->Text) + (Edit5->Text * Edit9->Text);
}
}
catch(...)
{
ShowMessage("Wystąpił błąd, proszę sprawdzić czy wszystkie pola są uzupełnione!");
}


To oczywiście nie cały kod, ale ogólnie chodzi o wyłapanie momentu, gdy pole EditX->Text jest puste ( normalnie jest na stałe wpisane 0 ze startem aplikacji ).
Powyższa funkcja powoduje, że czasem pojawia się: "Abnormal program termination".
Liberae sunt nostrae cogitationes
Avatar użytkownika
nvdante
Intelektryk
Intelektryk
 
Posty: 188
Dołączył(a): piątek, 12 czerwca 2009, 16:58
Lokalizacja: Dom
Podziękował : 8
Otrzymał podziękowań: 0
System operacyjny: Windows 7
Kompilator: VS 2015
Gadu Gadu: 0
    Windows XPFirefox

Re: Zapis białych znaków do pliku

Nowy postprzez Cyfrowy Baron » środa, 16 marca 2011, 19:44

nvdante napisał(a):Powyższa funkcja powoduje, że czasem pojawia się: "Abnormal program termination".


Właściwość Text obiektu typu TEdit jest typu String, nie jest więc liczbą lecz łańcuchem znaków, a Ty mnożysz te łańcuchy, więc nic dziwnego, że czasami występuje błąd. Mnożenie może występować tylko w sytuacji, gdy pola Edit zawierają tekst będący liczbą, jednak gdy takie pole zawiera tekst liczbą nie będący, to mamy błąd. Gdybyś dokonywał konwersji tekstu na liczby, to w przypadku, gdy tekst nie jest liczbą otrzymałbyś komunikat o błędzie konwersji. Ponieważ jednak nie zastosowałeś konwersji to masz ogólny błąd programu.
To co zrobiłeś jest niedopuszczalne. Należy zastosować konwersję.

Jeżeli pola Edit zawierają liczby całkowite, czyli typu int, to można sobie darować komunikaty, lub zastosować jeden komunikat, o tym, że coś we wszystkich polach brak tekstu. Dokonując konwersji z wykorzystaniem funkcji ToIntDef można podać wartość domyślną np. 1 tam gdzie konwersja nie jest możliwa, czyli w sytuacji gdy pole jest puste lub zawiera tekst nie będący liczbą:

KOD cpp:     UKRYJ  
if(Key == VK_RETURN)
{
  String testEdit = "";
  for(int i = 1; i <= 9; i++) /* 10 - Edit1 ... Edit9 */
    testEdit = testEdit + dynamic_cast<TEdit *>( FindComponent("Edit" + (String)i) )->Text.Trim();
   
  if( testEdit.IsEmpty() )
      ShowMessage("Wprowadź prawidłowe dane");
  else
     Edit10->Text = IntToStr( ( ToIntDef(Edit1->Text, 1) * ToIntDef(Edit9->Text, 1) ) +
                              ( ToIntDef(Edit3->Text, 1) * ToIntDef(Edit9->Text, 1) ) +
                              ( ToIntDef(Edit5->Text, 1) * ToIntDef(Edit9->Text, 1) )
                            );
}


Funkcja IntToStr konwertuje ponownie liczby na łańcuch znaków, gdyż jak wspomniałem właściwość Text obiektu TEdit oczekuje zmiennej typu String a nie int.
By uniknąć problemów z wprowadzaniem nieprawidłowych danych do pół Edit można wprowadzić ograniczenie pozwalające na wprowadzanie tylko liczb i ewentualnie przecinka dla liczb zmiennopozycyjnych. W tym celu tworzymy prostą funkcję sprawdzającą i wywołujemy ją w zdarzeniu OnKeyPress dla Edit1:

KOD cpp:     UKRYJ  
void OnlyDigit(char &Key)
{
 if(!strchr("0123456789,", Key) && Key != VK_BACK )
 {
  Key = 0;
 }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
 OnlyDigit(Key);
}


W ten sposób do Edit1 można wprowadzać tylko liczby całkowite i zmiennopozycyjne, ale co z pozostałymi Edit'ami. Dla nich też można utworzyć funkcję OnKeyPress, ale to zbędny kod, lepiej posłużyć się polimorfizmem. Czy dla pozostałych Edit'ów w Object inspector na zakładce Evensts w polu OnKeyPress wybieramy Edit1KeyPress. Postępujemy tak z każdym Edit'em, który chcemy podłączyć pod zdarzenie OnKeyPress obiektu Edit1. W ten sposób wszystkie obiekty korzystają z jednego zdarzenia tak samo jak by to było ich własne zdarzenie. Więcej o polimorfizmie w serwisie Cyfrowy Baron w dziale: teoria - rzutowanie typów, przypisanie polimorficzne.
onkeypress.png
Nie masz wystarczających uprawnień, aby zobaczyć pliki załączone do tego postu.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Zapis białych znaków do pliku

Nowy postprzez nvdante » środa, 16 marca 2011, 20:07

KOD cpp:     UKRYJ  
if(!strchr("0123456789,", Key))

A jak do tego ciągu dodać na przykład Backspace ( Del działa, Backspace nie )?
Czy jeśli użyłbym właśnie takiej "filtracji" wprowadzanych danych to można by było pominąć część odnośnie mnożenia Editów? Bo wiadomo dzięki temu, że zawsze będzie wprowadzona liczba.
Liberae sunt nostrae cogitationes
Avatar użytkownika
nvdante
Intelektryk
Intelektryk
 
Posty: 188
Dołączył(a): piątek, 12 czerwca 2009, 16:58
Lokalizacja: Dom
Podziękował : 8
Otrzymał podziękowań: 0
System operacyjny: Windows 7
Kompilator: VS 2015
Gadu Gadu: 0
    Windows XPFirefox

Re: Zapis białych znaków do pliku

Nowy postprzez Cyfrowy Baron » środa, 16 marca 2011, 20:28

Już poprawiłem poprzedni kod, ale powtórzę go jeszcze raz:

KOD cpp:     UKRYJ  
void OnlyDigit(char &Key)
{
 if(!strchr("0123456789,", Key) && Key != VK_BACK )
 {
  Key = 0;
 }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, char &Key)
{
 OnlyDigit(Key);
}




Jeżeli chcesz, żeby pola Edit zawierały zawsze jakąś wartość domyślną, nawet gdy użytkownik ją usunie to posłuż się zdarzeniem OnExit dla Edit1:

KOD cpp:     UKRYJ  
void __fastcall TForm1::Edit1Exit(TObject *Sender)
{
 if( ((TEdit*)Sender)->Text.IsEmpty() ) ((TEdit*)Sender)->Text = "0";
}


Inne obiekty typu TEdit podłączasz pod to zdarzenie tak jak to opisałem w swoim poprzednim poście.
Nie wiem czy wartość domyślna 0 to dobra wartość, gdyż cokolwiek pomnożysz przez 0 zawsze da 0. Jeżeli pola Edit zawierają domyślnie 0, to funkcji ToIntDef też należałoby przekazać jako wartość domyślną 0, a nie 1:

KOD cpp:     UKRYJ  
     Edit10->Text = IntToStr( ( ToIntDef(Edit1->Text, 0) * ToIntDef(Edit9->Text, 0) ) +
                              ( ToIntDef(Edit3->Text, 0) * ToIntDef(Edit9->Text, 0) ) +
                              ( ToIntDef(Edit5->Text, 0) * ToIntDef(Edit9->Text, 0) )
                            );


Jeżeli Edit9 tak często się powtarza to należałoby stworzyć dla tego obiektu zmienną typu int i mnożyć przez tą zmienną.

KOD cpp:     UKRYJ  
if(Key == VK_RETURN)
{
  String testEdit = "";
  for(int i = 1; i <= 9; i++) /* 10 - Edit1 ... Edit9 */
    testEdit = testEdit + dynamic_cast<TEdit *>( FindComponent("Edit" + (String)i) )->Text.Trim();
   
  if( testEdit.IsEmpty() )
      ShowMessage("Wprowadź prawidłowe dane");
  else
  {
     int edit9 = ToIntDef( Edit9->Text, 0);

     Edit10->Text = IntToStr( ( ToIntDef(Edit1->Text, 0) * edit9 ) +
                              ( ToIntDef(Edit3->Text, 0) * edit9 ) +
                              ( ToIntDef(Edit5->Text, 0) * edit9 )
                            );
  }
}


Jeżeli nie chcesz by konwersja zwracała wartość domyślną, gdy nie można dokonać konwersji to możesz użyć funkcji ToInt(), lub StrToInt:

KOD cpp:     UKRYJ  
int x = Edit1->Text.ToInt();
int y = StrToInt( Edit1->Text );


W takim przypadku w sytuacji gdy pole Edit nie zawiera tekstu będącego liczbą program zwróci komunikat o błędzie konwersji w języku angielskim. W środowisku C++Builder od wersji 2006 wzwyż jest dostępne specjalne narzędzie, które pozwala przetłumaczyć wszystkie lub wybrane komunikaty na język polski, wtedy nie trzeba przechwytywać komunikatu, gdyż program wyświetli taki komunikat po polsku o ile go sobie przetłumaczysz.
Błędy konwersji możesz przechwytywać za pomocą bloku try catch, ale musisz go precyzyjnie umieścić, nie tak jak zrobiłeś to w swoim przykładzie.


KOD cpp:     UKRYJ  
if(Key == VK_RETURN)
{
  String testEdit = "";
  for(int i = 1; i <= 9; i++) /* 10 - Edit1 ... Edit9 */
    testEdit = testEdit + dynamic_cast<TEdit *>( FindComponent("Edit" + (String)i) )->Text.Trim();
   
  if( testEdit.IsEmpty() )
  {
      ShowMessage("Wprowadź prawidłowe dane");

      return; /* przerwij gdyż dalsze wyliczenia nie mają sensu */
  }
  else
  {
     int edit9 = 0, edit1 = 0, edit3 = 0, edit5 = 0;

     try{
           edit9 = Edit9->Text.ToInt();
           edit1 = Edit1->Text.ToInt();
           edit3 = Edit3->Text.ToInt();
           edit5 = Edit5->Text.ToInt();
         }
         catch(...)
         {
           ShowMessage("Nieprawidłowa wartość");
           
           return; /* przerwij gdyż dalsze wyliczenia nie mają sensu */
         }

     Edit10->Text = IntToStr(  ( edit1 * edit9 ) + ( edit3 * edit9 ) + ( edit5 * edit9 )  );
  }
}
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    Windows XPFirefox

Re: Zapis białych znaków do pliku

Nowy postprzez nvdante » czwartek, 17 marca 2011, 13:18

Rewelacja, dokładnie o to chodziło, w sumie zastosowałem część kodu, ale myślę, że wyłapuje to wszelkie możliwości powstania problemów.
Dziękuję :-)
Liberae sunt nostrae cogitationes
Avatar użytkownika
nvdante
Intelektryk
Intelektryk
 
Posty: 188
Dołączył(a): piątek, 12 czerwca 2009, 16:58
Lokalizacja: Dom
Podziękował : 8
Otrzymał podziękowań: 0
System operacyjny: Windows 7
Kompilator: VS 2015
Gadu Gadu: 0
    Windows XPFirefox


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 6 gości