CYFROWY BARON • PROGRAMOWANIE • Zobacz wątek - StrToInt dla kilku liczb
Strona 1 z 2

StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 15:26
przez manin
Witam.
Dopiero co zacząłem pracę z C++ Builder i natknąłem się na problem, z którym nie umiem sobie poradzić.
Stworzony przeze mnie StringGrid w przechowuje w komórkach jednej z kolumn ciąg liczb rozdzielonych spacjami, np:
1 4 15 62 8
Dodatkowo ilość liczb w takim Stringu nie zawsze jest jednakowa.
W jaki sposób można (najprościej i najczytelniej dla laika) liczby z takiego ciągu zapisać w jednowymiarowej tablicy?
Dodatkowo, czy istnieje sposób, aby każdy znak, niebędący cyfrą, który przez nieuwagę użytkownika znalazłby się w komórce, zastąpić spacją?

Z góry dziękuję za wskazówki.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 16:05
przez Mironas
Przepisanie liczb ze stringa do tablicy, zakładając dla uproszczenia, że w stringu nie ma więcej niż 10 liczb można zrobić np tak:
KOD cpp:     UKRYJ  
  TStrings* lista = new TStringList();
  lista->LineBreak = " ";
  lista->Text =  "1 2 5 10 15 25";

  int tab[10];  // max 10 liczb
  for (int i=0 ; i<lista->Count ; i++)
    tab[i] = lista->Strings[i].ToInt();

  delete lista;
 

Jeśli nie wiesz ile maksymalnie może być liczb to musisz tworzyć tablicę dynamicznie.

Zamiana w tekście znaków innych niż cyfry na spacje:
KOD cpp:     UKRYJ  
  String tekst = "1a2 5 10 z15 25x";
  for (int i=1 ; i<=tekst.Length() ; i++)
    if ( tekst[i]<'0' || tekst[i]>'9' )
      tekst[i] = ' ';   // tutaj spacja


Przy takich operacjach pomocna może też być funkcja StringReplace(...).

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 16:40
przez manin
Dzięki za szybką odpowiedź, jednak chyba robię coś nie tak, bo przy kompilacji wyskakuje błąd 'LineBreak' is not member of 'TStrings'.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 17:07
przez Cyfrowy Baron
Wszystko robisz dobrze, tylko w C++Builder 6 TStringList nie ma metody LineBreak. W podanych niżej przykładach użyto tej metody by łamać tekst po każdej spacji, czyli taki zapis:

KOD text:     UKRYJ  
"1, 2 5, 10 15"


po użyciu LineBreak będzie wyglądał tak:

KOD text:     UKRYJ  
1
2
5
10
15

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 18:22
przez manin
W takim razie jakiej metody mógłbym użyć, ewentualnie w jaki sposób zmodyfikować tę, aby działała w "szóstce"?

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 19:01
przez Mironas
Dla BCB6 można tak:
KOD cpp:     UKRYJ  
  TStrings* lista = new TStringList();
  String tekst = "1 2 5 10 15 25";
 
  // zamiana spacji na znaki końca wiersza
  lista->Text = StringReplace(tekst, " ", "\r\n", TReplaceFlags() << rfReplaceAll);

  int tab[10];
  for (int i=0 ; i<lista->Count ; i++)
    tab[i] = lista->Strings[i].ToInt();

  delete lista;
 

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 19:01
przez Cyfrowy Baron
Trzeba parsować lub posłużyć się regex'em. Napiszę w wolnej chwili parsowanie, ale nie dziś.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 19:08
przez polymorphism
Regex do tego?! Niech użyje klasy istringstream.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 20:07
przez Cyfrowy Baron
polymorphism napisał(a):Niech użyje klasy istringstream.


A to mi akurat nie przyszło do głowy. :oops:

KOD cpp:     UKRYJ  
#include <iostream>
#include <fstream>
#include <sstream>

using namespace std;

 AnsiString text = "1 14 19 20";

 istringstream iSS( text.c_str() ); /* lub iSS( StringGrid1->Cell[x][y].c_str() ) */
 int n;

 while(iSS >> n)
 {
  ListBox1->Items->Add(n); /* tutaj przepisujesz ze zmiennej n gdzie ci potrzeba */
 }


Tyle, że przeszukiwanie zostanie przerwane gdy napotka tekst zamiast liczby. Można więc posłużyć się metodą isdigit:

KOD cpp:     UKRYJ  
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"


#include <sstream>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

using namespace std;

TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
 AnsiString text = "1 14 text 19 20";

 istringstream iSS( text.c_str() );
 char *buf;

 while(iSS >> buf)
 {

  if( isdigit( (unsigned char)*buf ) )
         ListBox1->Items->Add(buf);
 }
}

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 20:47
przez manin
Dziękuję bardzo wszystkim. Dzięki wam mogę ruszać dalej. Kluczem okazał się isdigit. Nie miałem pojęcia o istnieniu takiej funkcji. Z resztą sobie już poradziłem.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 20:49
przez polymorphism
KOD cpp:     UKRYJ  
 char *buf; //<--- na co wskazuje?

 while(iSS >> buf) { ... }

Zamiast kombinować z gołymi wskaźnikami, użyj po prostu std::stringa, i wtedy testuj pierwszy znak.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 20:51
przez Cyfrowy Baron
Ale on pracuje na tabeli StringGrid i typie AnsiString, więc jak da std::string to będzie musiał kombinować z konwersją.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 20:56
przez polymorphism
Z tego co pamiętam, to nie - są zdefiniowane operatory << >> dla AnsiStringa.

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 21:01
przez Cyfrowy Baron
Nie wiem o co Tobie chodziło w ostatniej wypowiedzi, ale można też ze string. W ten sposób pozbędę się pustego wskaźnika:

KOD cpp:     UKRYJ  
 AnsiString text = "1 14 text 19 20";

 istringstream iSS( text.c_str() );
 string buf;

 while(iSS >> buf)
 {
  if( isdigit( *buf.c_str() ) )
         ListBox1->Items->Add( buf.c_str() );
 }


Można by też od razu na AnsiString:

KOD cpp:     UKRYJ  
 AnsiString text = "1 14 text 19 20";

 istringstream iSS( text.c_str() );
 AnsiString buf;

 while(iSS >> buf.c_str())
 {
  if( isdigit( *buf.c_str() ) )
         ListBox1->Items->Add( buf );
 }

Re: StrToInt dla kilku liczb

Nowy postNapisane: poniedziałek, 2 grudnia 2013, 21:27
przez polymorphism
KOD cpp:     UKRYJ  
while(iSS >> buf.c_str())

To jest zła konstrukcja. Zawartość zwrócona przez c_str jest tylko do odczytu.

Nie wiem o co Tobie chodziło w ostatniej wypowiedzi (...)

O to mi chodziło, że AnsiString ma zdefiniowane operatory << >> dla strumieni standardowych, dzięki czemu nic nie musisz konwertować.

Z dokumentacji:
To utilize the C++ streaming operators (<< and >>) with AnsiString, you must use #include <iostream> or #define VCL_IOSTREAM before #include <dstring.h>. This occurs automatically if you include vcl.h or system.h and defines the operators as follows:

KOD cpp:     UKRYJ  
ostream& operator << (ostream& os, const AnsiString& arg);
istream& operator >> (istream& is, AnsiString& arg);