Zapisywanie zawartości Label do pliku .txt

0

Witam.
Chcę zapisać zawartość Label do pliku .txt funkcją SaveToFile ().

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    Label1 -> Caption -> SaveToFile ("c/Timer/plik.txt");
}

Wyskakuje mi "Pointer to structure required on left side of -> or ->
Gdy stworzę wskaźnik:

AnsiString Content = Label1 -> Caption;
AnsiString *ContPoint = &Content;
Label1 -> *ContPoint -> SaveToFile ("c/Timer/plik.txt");

To wyskakuje mi Member identifier expected oraz 'ContPoint' is assigned a value that is never used
Co zrobić w takiej sytuacji?

4

Poczytać jakiś kurs, bo najwyraźniej nie rozumiesz czym są wskaźniki, wartości oraz klasy.

Label1->Caption.SaveToFile("c/Timer/plik.txt");`

powinno działać. Alternatywnie:

AnsiString *ContPoint = &Label1->Caption;
ContPoint->SaveToFile("c/Timer/plik.txt");

btw: oszczędzaj spacje, mało czytelnie się robi jak stawiasz obok -> lub przed nawiasami.

0

Pierwszej metody już próbowałem wcześniej, ale wyskakiwały mi takie errory:
SaveToFile is not a member of AnsiString
2 metoda również nie działa:
Must take address of a memory location
'SaveToFile' is not a member of 'AnsiString'
'ContPoint' is assigned a value that is never used

2

W tym momencie odesłałbym do kursu angielskiego. Próbujesz wywołać metodę, która nie istnieje. Skąd wziąłeś to SaveToFile?

0

Faktycznie, masz rację, głupi błąd :P Brałem to z SaveDialog (tak myślałem). W takim razie jak zapisać zawartość Label? Metodą z SaveDialog? SaveToFile chyba jest tylko w RichEdit i Memo.

3

Użyłbym zwykłych fstreamów z C++. AnsiString ma metodę c_str().

Swoją drogą: dlaczego BCB, a nie Qt czy coś innego nowoczesnego?

0

Masz na myśli stworzenie wskaźnika typu fstream?

fstream *ContPoint = &Label1->Caption;

I potem analogicznie

ContPoint->c.str("c/Timer/plik.txt");

Używam BCB ponieważ Pan Zelent w swoich poradnikach zaczął go używać. Myślę, że jak już w miarę ogarnę BCB to przerzucę się na coś innego.

1

Ok, kolejny błąd. Kursów C++ pana Z. nie polecamy (https://4programmers.net/Forum/1226486). A BCB6 ma 14 lat, wiesz...

Co do "tworzenie wskaźnika typu fstream" - serio, weź się za kurs, ale inny.

http://en.cppreference.com/w/cpp/io/basic_ofstream

ofstream file("plik.txt");
file << Label1->Caption.c_str();
0

Ok. Zaincludowałem bibliotekę "fstream.h"
Wygląda to tak:

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    ofstream file("c/Timer/plik.txt");
    file << Label1->Caption.c_str();
}

Odpalam program, wyłączam. Wchodzę w dysk c, lecz nie ma tam żadnego katalogu Timer.

3
  1. Nie używaj niestandardowych bibliotek. <fstream> a nie fstream.h.
    #C/Timer/plik.txt nie jest poprawną ścieżką (nie ma :).
  2. fstream nie utworzy Ci drzewa katalogów. Jak się nie uda otworzyć pliku do zapisu to się nie uda.
0

Wypisz dane na stdout, a potem przekieruj to do pliku.

0

1.Próbowałem użyć

<fstream>

lecz wywala mi wtedy errory. Zapewne musiałbym zainstalować tą bibliotekę.
2.

ofstream file("C:\\Timer\\timer.txt");

Tak może być?
3.No właśnie. Żeby zapisać plik do danego folderu to muszę najpierw ten plik stworzyć. Jaki jest sposób, żeby mi samo stworzyło ten folder? Sposób, że wybieram folder raczej słabo wygląda, gdyż to ma działać nawet gdy nie zamknę programu, a komputer wyłączę.

1

<fstream> to nagłówek biblioteki standardowej C++ od 1998, dołączanej razem z kompilatorem. Gdybyś tylko używał czegoś z chociażby ostatniej dekady...

Pliku nie musisz tworzyć. Ale folder tak.

0

Wiem, że muszę tworzyć folder "ręcznie", ale znasz jakiś sposób, żeby ten folder tworzył się samoistnie? Jeśli podajesz taką

ofstream file("C:\\Timer\\timer.txt");

ścieżkę to sprawdza czy foldery istnieją. Jeśli nie to je tworzy.

@Edit
Ok, już wiem żeby użyć funkcji CreateDirectory (). Żeby to zrobić, to najpierw muszę to wsadzić do if sprawdzającego czy nie istnieje tam już taki folder (czyli czy już wcześniej zamykałem aplikację i folder już został stworzony). Jak to sprawdzić?

@Edit2
Stworzyłem coś takiego:

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    if (PathFileExists("C:\\Timer"))
    {
       ofstream file("C:\\Timer\\sesja.txt");
    }
    else
    {
       CreateDirectory("C:\\Timer" ,NULL);
       ofstream file("C:\\Timer\\sesja.txt");
    }
    file << Label1->Caption.c_str();        //Improper use of typedef 'file'

    file.close();                           //Improper use of typedef 'file'
}
0

Trzeba było użyć open dialog czy jak to się tam nazywa (z +10 lat nie używałem BCB).
Wpisywanie twardo takich ścieżek jest bardzo nieporęczne.

0

W OpenDialog nie jest tak, że sam musisz wybrać "ręcznie" ścieżkę pliku? Byłoby to nieprzydatne w momencie gdy np. wyłączę program bez wyłączenia komputera.

0
Lich555 napisał(a):

Wiem, że muszę tworzyć folder "ręcznie", ale znasz jakiś sposób, żeby ten folder tworzył się samoistnie? Jeśli podajesz taką

ofstream file("C:\\Timer\\timer.txt");

ścieżkę to sprawdza czy foldery istnieją. Jeśli nie to je tworzy.
A próbowałeś szukać albo czytać dokumentację? http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/SysUtils_ForceDirectories.html Jeśli ta funkcja nie jest u Ciebie dostępna to napisz sobie. Moduł SysUtils zawiera pomocne do tego funkcje. Generowanie drzewa katalogów można napisać rekurencyjnie w dość prosty sposób.

0

Potrzebuję teraz przekazać zmienne z 1 funkcji do 2.

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
int pierwszaZmienna, dwa, trzy;
//Z tej funkcji
//Coś w ten deseń?
FormClose(pierwszaZmienna, dwa, trzy)
} 
void __fastcall TForm1::FormClose(int jeden, int dwa, int trzy, TObject *Sender, TCloseAction &Action)
{
//Do tej
}
0

@Lich555 to tak nie działa. Zdarzenie OnClose musi mieć odpowiednie parametry i koniec. Poza tym nie wywołuj zdarzenia OnClose sam. Lepiej zrób:

this->Close();

Inaczej to zaciemnianie kodu.

Co do Twojego głównego problemu zmienne

int 1zmienna, dwa, trzy;

zadeklaruj jako nie lokalne z metodzie Timer1Timer, ale jako składowe TForm1 w sekcji private i już.

Poza tym zmień nazewnictwo zmiennych, bo mało to mówi...

0

Na samej górze programu mam coś takiego:

__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}

Tutaj mam stworzyć te zmienne?

1

Nie, poczytaj trochę pierwszy lepszy kurs C++ albo C++ Buildera i będziesz wiedział jak się deklaruje składowe klasy. Musisz to zrobić w następujący sposób. W pliki *.h robisz tak:

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
  TTimer *Timer1;
  void __fastcall Timer1Timer(TObject *Sender);
  void __fastcall OnClose(TObject *Sender, TCloseAction &Action);
private:	// User declarations
  int pierwszaZmienna, dwa, trzy;
public:		// User declarations
  __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Natomiast w pliku *cpp tak:

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  pierwszaZmienna = 1;
  dwa = 2;
  trzy = 3;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::OnClose(TObject *Sender, TCloseAction &Action)
{
  if (pierwszaZmienna == 1 && dwa == 2 && trzy == 3)
  ...
}
//---------------------------------------------------------------------------
0

Stworzyłem coś takiego...

void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
    s++;

    AnsiString hrs, mins, secs;

    hours = s / 3600;
    hrs = IntToStr (hours);
    if (hours < 10) hrs = "0"+hrs;

    minutes = (s - hours * 3600) / 60;
    mins = IntToStr (minutes);
    if (minutes < 10) mins = "0"+mins;

    seconds = s - hours * 3600 - minutes * 60;
    secs = IntToStr (seconds);
    if (seconds < 10) secs = "0"+secs;

    Label1 -> Caption = hrs+":"+mins+":"+secs;

    HrsAll = hours + HrsAll;
    MinsAll = minutes + MinsAll;
    SecsAll = seconds + SecsAll;
}

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
    AnsiString hrs, mins, secs;

    hrs = IntToStr (HrsAll);
    if (HrsAll < 10) hrs = "0"+hrs;

    mins = IntToStr (MinsAll);
    if (MinsAll < 10) mins = "0"+mins;

    secs = IntToStr (SecsAll);
    if (SecsAll < 10) secs = "0"+secs;

    if (!DirectoryExists("C:\\Timer"))
    {
       CreateDirectory("C:\\Timer", NULL);
    }

    ofstream file1("C:\\Timer\\sesja.txt");

    file1 << Label1->Caption.c_str();

    file1.close();

    ofstream fileAll("C:\\Timer\\lacznie.txt");

    fileAll << hrs.c_str() << ":" << mins.c_str() << ":" << secs.c_str();

    fileAll.close();
}

... i nie działa (mam na myśli tą część gdzie liczy łączny czas spędzony na komputerze.
1.Włączyłem program na 5 sekund i go zamknąłem.
1.1.W pliku .txt zobaczyłem "00:00:15"
2.Włączyłem program drugi raz na 5 sekund i go zamknąłem.
2.1.W pliku .txt zobaczyłem "00:00:15". Tak samo jak za pierwszym razem.

Nie pamiętam na ile czasu wtedy włączyłem program, ale zdarzyło mi się ujrzeć w pliku .txt "00:00:210"

2

Ogólnie to mam dla Ciebie trochę wskazówek odnośnie tego problemu.

  1. nie zapisuj do pliku wartości formatowanej w postaci hh:mm:ss, a coś bardziej elementarnego. Ja bym zapisywał po prostu sekundy. O wiele łatwiej wczytać oraz przetwarzać integera aniżeli tak formatowany string.
  2. skoro korzystasz z Builder'a to czemu nie wykorzystać mechanizmów jakie on dostarcza? Mam tu na myśli klasę TIniFile Pozwala ona w bardzo wygodny sposób wczytywać oraz zapamiętywać zmienne/ustawienia programu pomiędzy jego uruchomieniami.

Zatem pokażę Ci jakbym ja to zrobił:
Unit1.h

//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:	// IDE-managed Components
  TLabel *Label1;
  TTimer *Timer1;
  void __fastcall Timer1Timer(TObject *Sender);
  void __fastcall OnClose(TObject *Sender, TCloseAction &Action);
private:	// User declarations
  int seconds;

  AnsiString FormatSeconds(int sec);
public:		// User declarations
  __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

Unit1.cpp

//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include <IniFiles.hpp> //TIniFile

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
  : TForm(Owner)
{
  TIniFile *IniFile;
  IniFile = new TIniFile("D:\\timer.ini");
  seconds = IniFile->ReadInteger("Timer","seconds",0);
  delete IniFile;

  Label1->Caption = FormatSeconds(seconds);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  seconds++;

  Label1->Caption = FormatSeconds(seconds);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::OnClose(TObject *Sender, TCloseAction &Action)
{
  TIniFile *IniFile;
  IniFile = new TIniFile("D:\\timer.ini");
  IniFile->WriteInteger("Timer","seconds",seconds);
  delete IniFile;
}
//---------------------------------------------------------------------------

AnsiString TForm1::FormatSeconds(int sec)
{
  int hrs, mins, secs;
  AnsiString retVal;

  hrs = sec / 3600;
  mins = (sec - hrs * 3600) / 60;
  secs = sec - hrs * 3600 - mins * 60;

  retVal.printf("%02d:%02d:%02d",hrs,mins,secs);
  return retVal;
}
//---------------------------------------------------------------------------

Oczywiście jest to podstawowa wersja bez obsługi błędów itp. Tak przy okazji to formatowanie też masz zrobione niespecjalnie. Te ify przy:

 if (minutes < 10) mins = "0"+mins;

są zupełnie niepotrzebne. Załatwiłem to taką formułką:

retVal.printf("%02d:%02d:%02d",hrs,mins,secs);

Prawda, że prostsze? :)

Ogólnie mój kod też nie jest idealny. Wypadałoby wydzielić samą obsługę pliku ze statystykami do oddzielnej klasy. Wygodne jest to jeśli chcesz testować np. różne sposoby zapisu poprzez TIniFile, streamy, czy bezpośrednio pisać do pliku. Zmieniasz wtedy tylko samą klasę, a nie ruszasz pozostałej części kodu. Nie mniej jednak masz coś na początek.

0

Ok, dzięki wielkie.
1.Mógłbyś wytłumaczyć co poszczególne argumenty znaczą w

ReadInteger("Timer","seconds",0)

oraz w WriteInteger("Timer","seconds",seconds);

?
2.Co to jest Timer w tych argumentach?
3.
```cpp
_fastcall TForm1::TForm1(TComponent* Owner)
void __fastcall TForm1::OnClose(TObject *Sender, TCloseAction &Action)
co robią te funkcje?</li> </ul> </li> </ol>
retVal.printf("%02d:%02d:%02d",hrs,mins,secs);

Gdzie jest to zabezpieczenie, żeby 0 nie wypisywało się jak liczba jest większa niż 10?

2

Ad1.
Każda metoda Write/Read z klasy TIniFile posiada pierwsze dwa argumenty:
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/IniFiles_TCustomIniFile_ReadInteger.html
http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/IniFiles_TCustomIniFile_WriteInteger.html

__fastcall Longint ReadInteger(const AnsiString Section, const AnsiString Ident, Longint Default);
__fastcall WriteInteger(const AnsiString Section, const AnsiString Ident, Longint Value);

Aby zrozumieć o co chodzi trzeba wiedzieć jak taki plik będzie wyglądał. W moim przypadku będzie on tak
''[Timer]
seconds=18''
Zatem popatrz:

ReadInteger("Timer","seconds",0)

Timer -> Section
seconds -> Ident
0 -> Default.

Każdy plik *.ini składa się ( https://pl.wikipedia.org/wiki/INI ) z sekcji oraz parametrów. Ja akurat nazwałem sekcję Timer a parametr seconds. Stąd takie, a nie inne parametry w metodzie ReadInteger. Default jest zwracane w przypadku kiedy w danym pliku nie będzie w danej sekcji żądanego przez nas parametru.

W przypadku metod Write trzeci parametr oznacza zmienną jaką chcemy zapisać do pliku.

Ad2. akurat tak sobie nazwałem równie dobrze można by było dać taki format pliku ini.
''[mojaSekcja]
mojBardzoWaznyParametr=18''
Wtedy wywołałbym metodę w taki sposób:

ReadInteger("mojaSekcja","mojBardzoWaznyParametr",0)

Ad.3

_fastcall TForm1::TForm1(TComponent* Owner)

jest to konstruktor klasy. Wywołuje się podczas tworzenia nowego obiektu. IDE tworzy zawsze konstruktor z pustym ciałem. Po utworzeniu pustej formatki powinien być z pliku *.cpp.

void __fastcall TForm1::OnClose(TObject *Sender, TCloseAction &Action)

Jest to zdarzenie które będzie się wywoływało podczas zamykania okna. U Ciebie nazwane jest to

void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)

Ad.4
Klucz leży w zrozumieniu jak działa funkcja printf. Przekazany string formatujący %02d (jego format znajdziesz np. tu opisany https://en.wikipedia.org/wiki/Printf_format_string#Syntax ) rozłożony na czynniki pierwsze oznacza:
''%[parameter][flags][width][.precision][length]type
%02d''
czyli czytamy
0 -> flag
2 -> width
d -> type

Flaga 0 oznacza uzupełnianie znakami 0 do osiągnięcia minimum n znaków, gdzie n określa kolejny parametr width, czyli w naszym przypadku 2. Dlatego formatuje dodając 0 do liczb 1-9. Dla liczby 190 wypisze 190. Wiem, że to może trochę zamotane. Ale jak sam widzisz ma duże możliwości.

0

O kur...cze. Dzięki wielkie. Opisałeś to w jasny sposób. Dzięki, że chciało Ci się to tłumaczyć. Jeszcze takie dopytanko odnośnie plików *ini. To 0 -> Default jest zwracane wtedy gdy my podamy jako drugi argument parametr jakiś (tutaj seconds), a w pliku go nie będzie?
@Edit
Aż muszę sobie screena zrobić z tego postu. Nie żartuję :D

1

Spoko, czemu miałbym nie wytłumaczyć? Każdy przecież kiedyś zaczynał :)

Odpowiadając na Twoje pytanie. Tak. Parametr Default będzie zwrócone w przypadku braku takiego parametru (ale w danej sekcji). Nie będzie. Warto zaznaczyć, że parametry mogą się powtarzać w ramach różnych sekcji. Tak więc znacząca jest para sekcja+parametr.

Lich555 napisał(a):

Aż muszę sobie screena zrobić z tego postu. Nie żartuję :D
Cóż można i tak ;)

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.