Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > friend std::istream &operator>> для класса строки


Автор: anatox91 16.5.2008, 13:07
подскажите пожалуйста как реализовать сабж. с operator<< собсно все легко, а вот этот оператор как-то не доганяю как реализовать
приват-секция моего класса:
Код

char *string;
unsigned int len;

пишу 
Код

friend std::istream &operator>>(std::istream& in, cstring &obj) {
              //...
              return in;
              }

а дальше че-то не доганяю, подскажите пожалуйста
заранее большое спасибо! с меня +  smile 

Автор: baldina 16.5.2008, 13:21
ну во первых cstring &obj не const - туда же записывается.

проще всего - через std::string

Код

friend std::istream &operator>>(std::istream& in, cstring &obj) {
              std::string s;
              in >> s;
              obj.len = s.size ();
              obj.string = new char[obj.len+1];
              strcpy (obj.string, s.c_str ());
              return in;
              }

Автор: anatox91 16.5.2008, 13:30
про конст забыл  smile 
через std::string я то знаю что можно сделать, интересно можно ли это сделать без классов типа std::string, CString

Автор: baldina 16.5.2008, 13:40
можно. вызывай в цикле in.getline () с временным буфером, перераспределяй память и копируй в свой char*

Автор: anatox91 16.5.2008, 13:52
ясно, попробую

Автор: anatox91 16.5.2008, 18:34
короче решил для начала написать функцию , которая добавляла бы в конец строки другую строку, чтобы меньше было возни с перераспределением памяти, вроде работает нормально, но почему-то всегда в конец добавляет символ пробела smile
вот код:
Код

cstring &append(const char *str) {
               if(!len) {
                        len = strlength(str); //my function for count string length
                        string = new char[len + 1];
                        unsigned int i=0;
                        for(; i < len; i++) {
                              string[i] = str[i];
                              }
                        string[i] = '\0';
                        }
               else {
                    unsigned int buf_len = len;
                    char *buf = new char[buf_len]; //creating buffer for copy string
                    unsigned int k=0; //iterator
                    for(; k < buf_len; k++) buf[k] = string[k]; //copying string to buffer
                    
                    delete[] string; //deleting old string
                    len += strlength(str); //my function for count string length
                    string = new char[len + 1]; 
                    for(k=0; k < buf_len; k++) { //copying characters from buffer
                             string[k] = buf[k];
                             }
                    delete[] buf; //clearing buffer
                    unsigned int c=0;
                    for(; k < len; k++, c++) { //copying characters from added string
                          string[k] = str[c];
                          }
                    string[k] = '\0'; //add null
                    return *this; //return new object
                    }
               }
  
и еще. 
baldina, я только не понимаю, какое тогда условие задать для цикла?

Автор: bsa 16.5.2008, 21:10
anatox91, а зачем ты лишний раз память выделял и освобождал? Кто тебе мешал сразу под buf выделить память, необходимую для хранения результирующей строки (строка 13). Тогда бы тебе не нужно было делать строки 19-23

Автор: anatox91 16.5.2008, 22:07
bsa, ты имел ввиду что-то типа такого ?
Код

else {
         len += strlength(str);
         char *buf = new char[len+1];
         unsigned int k=0; 
         for(; string[k]; k++) buf[k] = string[k];
                    
         unsigned int c=0;
         for(; k < len; k++, c++) { 
                     buf[k] = str[c];
                          }
         buf[k] = '\0';
         delete[] string;
         string = buf;
         return *this; 
           }


Автор: bsa 16.5.2008, 22:23
anatox91, да.

Автор: anatox91 16.5.2008, 22:38
а можно поподробнее насчет цикла smile 
Цитата(baldina @  16.5.2008,  13:40 Найти цитируемый пост)
вызывай в цикле in.getline () с временным буфером, перераспределяй память и копируй в свой char*

и какое условие ставить я чет не пойму... 



Автор: bsa 16.5.2008, 23:01
anatox91, getline() читает строку до тех пор, пока не будет достигнут конец строки, конец файла, либо не заполнится предоставленный буфер.

Автор: opjox 17.5.2008, 21:25
Код

#include <iostream>

class cstring {
public:
  cstring() 
  {
    len=0; 
    buf=0;
  }

  friend std::istream& operator>>(std::istream &in, cstring &obj)
  {
    std::istream::sentry s(in);       // ждем, пока в буфере будет хотя бы что-то
    if(s) {
      std::streambuf *p = in.rdbuf(); // получаем указатель на буфер с данными
      obj.len = p->in_avail();        // получаем, сколько сейчас можно считать из буфера
      
      if(obj.buf) delete [] obj.buf;
      obj.buf = new char[obj.len];

      for(size_t i=0; i<obj.len; i++) obj.buf[i] = p->sbumpc(); // копируем в наш буфер
      obj.buf[obj.len] = 0;
    }
    return in;
  }

  friend std::ostream& operator<<(std::ostream &out, const cstring &obj)
  {
    out << obj.buf;
    return out;
  }

private:
  char * buf;
  size_t len;
};

int main()
{
  cstring a;
  std::cin >> a;
  std::cout << a;
  return 0;
}


[+] выделение памяти и копирование происходит только один раз
[-]  данная реализация не обращает внимания на символы-разделители, читает все скопом в память

Автор: opjox 17.5.2008, 21:54
Цитата(opjox @  17.5.2008,  21:25 Найти цитируемый пост)
[-]  данная реализация не обращает внимания на символы-разделители, читает все скопом в память

пофиксил:

Код

class cstring {
public:
  cstring(): separators(" \n\t")
  {
    len=0; 
    buf=0;
  }

  friend std::istream& operator>>(std::istream &in, cstring &obj)
  {
    std::istream::sentry s(in);       // ждем, пока в буфере будет хотя бы что-то
    if(s) {
      std::streambuf *p = in.rdbuf(); // получаем указатель на буфер с данными
      size_t len = p->in_avail();        // получаем, сколько сейчас можно считать из буфера
      
      // узнаем, сколько символов до разделителя
      //
      obj.len = 0;                 
      while(len && obj.it_not_separator(p->sbumpc())) {
        len--;
        obj.len++;
      }
      // узнали, сколько символов до разделителя

      for(len=0; len<=obj.len; len++) p->sungetc(); // теперь вернемся к первому символу
      
      if(obj.buf) delete [] obj.buf;
      obj.buf = new char[obj.len+1];

      for(size_t i=0; i<obj.len; i++) obj.buf[i] = p->sbumpc(); // копируем в наш буфер
      obj.buf[obj.len] = 0;
    }
    return in;
  }

  friend std::ostream& operator<<(std::ostream &out, const cstring &obj)
  {
    out << obj.buf;
    return out;
  }

private:
  bool it_not_separator(char c)
  {
    const char *p = separators;
    for(;*p;p++) if(*p==c) return false;
    return true;
  }

  const char *separators; // все возможные символы-разделители

  char *buf;
  size_t len;
};

Автор: anatox91 18.5.2008, 10:38
чет не работает  smile 
после ввода строки сразу закрывается прога:
Код

cstring s;
std::cin >> s;
std::cout << s;
std::cin.get();

и если так, то тоже не выводит ничего после ввода:
Код

cstring first("example:");
cstring second;
std::cout << first;
std::cin >> second;
std::cout << second;
system("pause");


Код

friend std::istream &operator>>(std::istream& in, cstring &obj) {
              std::istream::sentry s(in);
              if(s) {
                    std::streambuf *p = in.rdbuf();
                    size_t len = p->in_avail();
                    
                    obj.len = 0;
                    while(len && obj.it_not_separator(p->sbumpc())) {
                              len--;
                              obj.len++;
                              }
                    for(len = 0; len <= obj.len; len++) p->sungetc();
                    
                    if(obj.string) delete[] obj.string;
                    obj.string = new char[obj.len + 1];
                    
                    for(size_t i = 0; i < obj.len; i++) obj.string[i] = p->sbumpc();
                    obj.string[obj.len] = '\0';
                    }
              return in;
              }

кстати а разве катит простой ноль в конце:
Цитата

Код

obj.string[obj.len] = 0;


вместо этого
Код

obj.string[obj.len] = '\0';

?

Автор: bsa 18.5.2008, 12:20
Цитата(anatox91 @ 18.5.2008,  10:38)
кстати а разве катит простой ноль в конце:
Цитата

Код

obj.string[obj.len] = 0;


вместо этого
Код

obj.string[obj.len] = '\0';

?

катит.
Но лучше типизировать (т.е. использовать '\0'), имхо.

Автор: anatox91 18.5.2008, 19:37
а почему не работает пример opjox'a можете подсказать? вроде разобрался в его примере и по-моему все должно работать... но не работает...

Автор: opjox 18.5.2008, 19:58
Цитата(bsa @  18.5.2008,  12:20 Найти цитируемый пост)
Но лучше типизировать (т.е. использовать '\0'), имхо.

'\0' – подчеркнет тип, но, имхо, для глаза 0 приятнее выглядит. На нижнем уровне присвоение будет выполнено одинаково.

Цитата(anatox91 @  18.5.2008,  19:37 Найти цитируемый пост)
а почему не работает пример opjox'a можете подсказать? вроде разобрался в его примере и по-моему все должно работать... но не работает...


Засада сидит в строчке:
Код

size_t len = p->in_avail();

если код собран с помощью g++, то для потока std::cin функция in_avail упорно возвращает 0, даже несмотря на то, что в буфере есть данные. Причем, на VS2008 код работает на ура. 

Чтобы все заработало, надо в len занести кол-ва данных в буфере. Или же модифицировать механизм поиска разделителя. 

Сейчас будет хоккей smile , после него попробую разобраться с проблемой. 

Автор: anatox91 18.5.2008, 21:56
попробовал, действительно на визуалке все фурычит, а на MinGW не хочет... 
opjox, спасибо кстати, очень интересные функции узнал, +

Автор: opjox 20.5.2008, 00:18
В силу обстоятельств smile , отвечаю только сейчас.

Решить проблему так и не смог. Попробовал найти замену in_avail(), но тщетно. Потом решил пойти другим путем – пройтись по всему буферу до разделителя или до окончания данных. Тут уже все пошло лучше и даже удалось написать рабочий код для VS2008:

Код

  friend std::istream& operator>>(std::istream &in, cstring &obj)
  {
    std::istream::sentry s(in);       // ждем, пока в буфере будет хотя бы что-то
    if(s) {
      std::streambuf *p = in.rdbuf(); // получаем указатель на буфер с данными
      
      // узнаем, сколько символов до разделителя
      //
      obj.len = 0;                 
      char a;
      while( (a = p->sgetc())!=EOF && obj.it_not_separator(a)) {
        obj.len++;
        p->sbumpc();
      }
      // узнали, сколько символов до разделителя

      for(size_t len=0; len<obj.len; len++) p->sungetc(); // теперь вернемся к первому символу
      
      if(obj.buf) delete [] obj.buf;
      obj.buf = new char[obj.len+1];

      for(size_t i=0; i<obj.len; i++) obj.buf[i] = p->sbumpc(); // копируем в наш буфер
      obj.buf[obj.len] = 0;
    }
    return in;
  }


Но у этого кода есть проблема: если мы достигнем не разделителя, а конца данных, то тогда происходит сброс всего буфера, из-за чего все данные в нем теряются. Но для g++ оказалось всю намного хуже и без этого.  Функция sungetc отрабатывала всего один раз, а остальные вызовы игнорировались. Из-за этого не удается достич начала данных.

Похоже, придется отказаться от этого варианта, не вижу я пока больше способов решения проблем этого пути. 

Автор: baldina 20.5.2008, 07:23
Что-то кажется мне с getline() попроще будет. Пусть даже и с неоднократным выделением памяти: производительность тут имхо не слишком важна ибо 
Цитата

интересно можно ли это сделать без классов 
 а в серьезной задаче будет std::string, т.к. велосипед уже изобретен и проверен.

Код

struct cstring {
  size_t len;
  char *string;
};

std::istream& operator>>(std::istream& in, cstring& obj)
{
  const size_t buf_size = 256;
  char buf[buf_size];
  obj.len = 0;
  obj.string = (char*)calloc(1,1);
  do
  {
    in.clear ();
    in.getline (buf, buf_size, ' ');
    size_t len = in.gcount ();
    if (len == 0)
      break;
    obj.string = (char*)realloc (obj.string, obj.len + len);
    strcpy (obj.string + obj.len, buf);
    obj.len += len;
  } while (in.fail ());

  return in;
}


Автор: anatox91 20.5.2008, 17:51
всем спасибо, решил все-таки через string пусть будет  smile 
хотя конечно странно что пример opjox'а не работает в MinGW, но в VS2008 работает...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)