Модераторы: Daevaorn
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Написание аналога atoi. или как улучшить код. 
:(
    Опции темы
maksr
  Дата 9.8.2006, 14:46 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Lizard King
*


Профиль
Группа: Участник
Сообщений: 60
Регистрация: 17.2.2005
Где: Нижний Новгород

Репутация: нет
Всего: 1



В книги Страуструпа было задание: написать аналог функции atoi.
У меня вот что получилось:

Код

int stringToInt(const char* string)
{
 int self = 0;
 int mult = 10;
 bool negative = false;
 int* values = new int[strlen(string)];
 int count = 0;
 for( int id = 0; id < strlen(string); id++ )
 {
  if( string[id] == '-' ) negative = true;
  for( char ch = '0'; ch <= '9'; ch++ )
   if( string[id] == ch )
    values[count++] = (int)( (int)ch - (int)'0' );
 };
 self = values[count - 1];
 for( int id = 0; id < count - 1; id++ ) mult = mult * 10;
 for( int id = 0; id < count - 1; id++ )
 { 
  mult = mult / 10;
  self = self + ( values[id] * mult );
 };
 if( negative == true ) 
  return -self;
 else
  return self;
};


Пока только для десятичных чисел. И главное что работает  smile . Как можно улучшить этот код? 

Ещё нашёл описание atoi в исходниках amos.

Код

int amos_stringToInt(char* string)
{
 long int self = 0;
 int sign = 1;
 
 while(*string == ' ' || (unsigned int)(*string - 9) < 5u) string++;
 
 switch(*string)
 {
  case '-': sign = -1;
  case '+': ++string;
 };
 
 while((unsigned int)(*string - '0') < 10u)
 {
  self = self * 10 + *string - '0'; ++string;
 };
 
 return sign == -1 ? - self : self;
};


Здесь короче, только ничего не понятно. smile 




PM MAIL ICQ   Вверх
sergejzr
Дата 9.8.2006, 15:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 19
Всего: 360



Код

int stringToInt(const char* string)
{
 int self = 0;
 int mult = 10;
 bool negative = false;
 int* values = new int[strlen(string)];
 int count = 0;
 for( int id = 0; id < strlen(string); id++ )
 {
  if( string[id] == '-' ) negative = true; //плохо! ты разрешил - в середине числа. То есть 345-99 выдаст тебе -34599. Проверка на минус должна быть только самого первого знака.

  for( char ch = '0'; ch <= '9'; ch++ )//тут ты всё собираешь в массив, когда можно сразу вырешивать..
   if( string[id] == ch )
    values[count++] = (int)( (int)ch - (int)'0' ); //
 };
 self = values[count - 1];
 for( int id = 0; id < count - 1; id++ ) mult = mult * 10;
 for( int id = 0; id < count - 1; id++ )
 { 
  mult = mult / 10;
  self = self + ( values[id] * mult );
 };
 if( negative == true ) 
  return -self;
 else
  return self;
};


Добавлено @ 15:09 
Код

int amos_stringToInt(char* string)
{
 long int self = 0;
 int sign = 1;
 //* string это указатель. На данный момент, он указывает на первую букву числа
 while(*string == ' ' || (unsigned int)(*string - 9) < 5u) string++;//здесь повидимому пропускаем пробелы итд.
 

 switch(*string) //Проверяется первый знак
 {
  case '-': sign = -1; //если первый знак - , число негативное
//break;  здесь не стоит, значит программ попадёт в следующее условие
  case '+': ++string; //Указатель указывает на вторую букву теперь (то есть - перескочили)
 };
 
 while((unsigned int)(*string - '0') < 10u) //Проверка на то, что знак число. Цикл будет выполнятся, пока указатель указывает на число
 {
  self = self * 10 + *string - '0'; ++string; //здесь каждый ответ умножается на 10 (таким образом первый знак умножится на 10^количество_знаков, второй на 10^количество_знаков-1, 3 на 10^количество_знаков-2 итд. )
 };
 
 return sign == -1 ? - self : self; // ну и наконец смотрим, негативное число, или нет
};



--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
maksr
Дата 9.8.2006, 15:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Lizard King
*


Профиль
Группа: Участник
Сообщений: 60
Регистрация: 17.2.2005
Где: Нижний Новгород

Репутация: нет
Всего: 1



Цитата(sergej.z @  9.8.2006,  15:02 Найти цитируемый пост)
плохо! ты разрешил - в середине числа.

Код

 if(string[0] == '-') negative = true; // если первый символ не будет ' ' 
 if((string[id-1] == ' ') && (string[id] == '-')) negative = true; // иначе


Цитата(sergej.z @  9.8.2006,  15:02 Найти цитируемый пост)
тут ты всё собираешь в массив, когда можно сразу вырешивать..

 Например?

Добавлено @ 15:24 
sergej.z, Спасибо разобрался не много.  smile 
PM MAIL ICQ   Вверх
sergejzr
Дата 9.8.2006, 15:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 19
Всего: 360



Например вот так:

Код

 while((unsigned int)(*string - '0') < 10u) //Проверка на то, что знак число. Цикл будет выполнятся, пока указатель указывает на число
 {
  self = self * 10 + *string - '0'; ++string; //здесь каждый ответ умножается на 10 (таким образом первый знак умножится на 10^количество_знаков, второй на 10^количество_знаков-1, 3 на 10^количество_знаков-2 итд. )
 };


Попробую "понятнее" расписать цикл:

Код

int resultat=0; //Вначале результат - 0
for( 
      char znak=*string;     //Зачитаваем в znak то, на что указывает указатель
      znak>'0'&&znak<'9';  //Цикл идёт, пока znak это действительно цифра
      string++,  znak=*string //сдвигаем указатель и зачитываем следующую букву
   ) 
{
int cifra=znak-'0'; //смотрим, чену же равна цифра
resultat=resultat*10+cifra; //и вычисляем результат
}

Таким образом для трёхзначного числа , цикл будет считать по фромуле

resultat=((((cifra1*10)+cifra2)*10)+cifra3)*10= //Ну, просчитаем ручками..
(((cifra1*10)+cifra2)*10)*10+cifra3*10=  //открываем скобки
((cifra1*10)+cifra2)*10*10+cifra3*10= //открываем скобки
((cifra1*10)+cifra2)*100+cifra3*10= //упрощаем
(cifra1*10)*100+cifra2*100+cifra3*10= //открываем скобки
cifra1*1000+cifra2*100+cifra3*10 //упрощаем


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
ptr
Дата 9.8.2006, 15:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 661
Регистрация: 31.5.2006
Где: Новосибирск

Репутация: 1
Всего: 14



Если хочешь улучшить код, то добавь проверку аргумента. Чтобы такое например 
Код

stringToInt("--");
stringToInt("10.0010");
stringToInt(NULL);
stringToInt("10001-0");
stringToInt("10001000000000000000");

возвращало скажем 0.


--------------------
Единственный способ определить границы возможного - это выйти за эти границы, в невозможное.
Артур Кларк.
PM MAIL ICQ   Вверх
MAKCim
Дата 9.8.2006, 15:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 52
Всего: 207



Код

int atoi(const char* str) throw(bad_convert)
{
    char current;
    int number=0, tail=1;
    const char* begin=str, *end=str+strlen(str)-1;
    bool flag=*str=='-';
    if (flag) ++begin;
    else if (*str<'0' || *str>'9') throw bad_convert;
    while (end>=begin)
    {
        if (*end<'0' || *end>'9') throw bad_convert;
        number+=(*end-- - 48)*tail;
        tail*=10;
    }
    return flag ? -number: number;
}


Добавлено @ 15:45 
Цитата

возвращало скажем 0. 

имхо, плохо
если в стиле C++ - лучше exception

Это сообщение отредактировал(а) MAKCim - 9.8.2006, 15:50


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
maksr
Дата 9.8.2006, 15:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Lizard King
*


Профиль
Группа: Участник
Сообщений: 60
Регистрация: 17.2.2005
Где: Нижний Новгород

Репутация: нет
Всего: 1



То All, Спасибо, буду разбираться.  smile  
PM MAIL ICQ   Вверх
ptr
Дата 9.8.2006, 16:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 661
Регистрация: 31.5.2006
Где: Новосибирск

Репутация: 1
Всего: 14



Цитата(MAKCim @  9.8.2006,  19:45 Найти цитируемый пост)
имхо, плохо
если в стиле C++ - лучше exception

Знаю что плохо. Просто я не заментил, что код был на C++ да и так делает atoi (хотя и не всегда). 

Надо было написать "корректно обрабатывались" чтобы не придирались smile smile  .

Это сообщение отредактировал(а) ptr - 10.8.2006, 05:15


--------------------
Единственный способ определить границы возможного - это выйти за эти границы, в невозможное.
Артур Кларк.
PM MAIL ICQ   Вверх
Oleg_Ci
Дата 9.8.2006, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Friend
**


Профиль
Группа: Участник
Сообщений: 485
Регистрация: 28.5.2006
Где: Новосиб.обл.

Репутация: 3
Всего: 30



Цитата(maksr @  9.8.2006,  18:46 Найти цитируемый пост)
Пока только для десятичных чисел. И главное что работает   . Как можно улучшить этот код?
Можно так smile , я твои строчки "ненужные" закоментировал
Код

int stringToInt(const char* string)    
{    
    //int self = 0;    
    //int mult = 10;    
    int negative = 1;    //<<<<<< bool negative = false;
    int values = 0;    //<<<<<<<  new int[strlen(string)];
    int count = 0;  //  счётчик

    for ( ; string[count] == ' '; count++ );  // пропускаем пробелы

    if( string[count] == '-' ) 
    {
        negative = -1;    // <<<<   = false;
        count++;
    }
    else if( string[count] == '+' ) count++;

    for( ; string[count]>='0' && string[count] <= '9'; count++ ) //<<<<       for( char ch = '0'; ch <= '9'; ch++ )    
         // if( string[id] == ch )        
            values = values*10 + string[count] - '0';  //<<<     values[count++] = (int)( (int)ch - (int)'0' );    

    /*
    self = values[count - 1];    
    for( int id = 0; id < count - 1; id++ ) mult = mult * 10;    
    for( int id = 0; id < count - 1; id++ )    
    {    
        mult = mult / 10;    
        self = self + ( values[id] * mult );    
    };
    if( negative == true )    
        return -self;    
    else    
        return self;    
        */
    values *= negative;
    return values;
};
Мой велосипедик smile 
Код

int StrToInt(const char* string)
{
    int result = 0;
    int flag = 1;
    while( *string == ' ' ) // пропускаем пробелы
        string++;
    if( *string == '-' ) 
    {    flag = -1;  string++;    }
    else if ( *string == '+' ) 
        string++;    

    for (; *string>='0' && *string<='9'; string++ )
    {
        result = result*10 + *string - '0';
    }
    result *= flag;
    return result;
}


Это сообщение отредактировал(а) Олег4 - 9.8.2006, 17:52
PM MAIL   Вверх
maksr
Дата 9.8.2006, 23:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Lizard King
*


Профиль
Группа: Участник
Сообщений: 60
Регистрация: 17.2.2005
Где: Нижний Новгород

Репутация: нет
Всего: 1



Переделал.  smile 
Код

int stringToInt(const char* string)
{
 int self = 0;
 bool negative = false;
 
 if(string == NULL) return 0;
 
 while(*string == ' ') string++;
 
 while((*string == '-')    || (*string == '+'))
 {
  (*string == '-') ? (negative = true) : (negative = false);
  string++;
 };
  
 while(*string != '\0')
 {
  while(*string == ' ') string++;
  
  if((*string <= '9') && (*string >= '0'))
  {
   if((self = self * 10 + *string - '0') >= INT_MAX - self)
   {
    return 0;
   }
   else string++;
  }
  else return 0;
 };
 return (negative == true) ? -self : self;
};


Добавлено @ 23:11 
Цитата(sergej.z @  9.8.2006,  15:36 Найти цитируемый пост)
znak>'0'&&znak<'9';

  smile 

PM MAIL ICQ   Вверх
ptr
Дата 10.8.2006, 05:57 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 661
Регистрация: 31.5.2006
Где: Новосибирск

Репутация: 1
Всего: 14



Код

stringToInt("1 2 3");
stringToInt("-+123");
stringToInt("2147483647");//INT_MAX



--------------------
Единственный способ определить границы возможного - это выйти за эти границы, в невозможное.
Артур Кларк.
PM MAIL ICQ   Вверх
sergejzr
Дата 10.8.2006, 12:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Un salsero
Group Icon


Профиль
Группа: Админ
Сообщений: 13285
Регистрация: 10.2.2004
Где: Германия г .Ганновер

Репутация: 19
Всего: 360



Код

 for((*string == '-')    || (*string == '+'); string++)
    if (*string == '-') 
            (negative = !negative ) ;// -- дают нам + 
//  :-P



PS:
Как всё-таки atoi красиво написана...


--------------------
PM WWW IM ICQ Skype GTalk Jabber AOL YIM MSN   Вверх
Dragon
Дата 10.8.2006, 17:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 73
Регистрация: 10.8.2006
Где: Киев

Репутация: нет
Всего: нет



Посвящяется не переписыванию кода atoi(), а улучшению кода по конверции типов в С++, потому что как не периписывай atoi() суть от этого не сильно изменяется smile Надеюсь не оффтоп.
В Boost'е есть такая маленькая функция - lexical_cast - кроссплатформенный аналог всем convert функциям.

Используется таким образом:

Код

// ...
std::string _some = "297.2283";
float _val = boost::lexical_cast<float>(_some);
// ...


Я написал также маленький вспомогательный класс для этих целей:
Код

#ifndef _CONVERT_STRING_H
#define _CONVERT_STRING_H

namespace std
{
    class convert_string : public std::string
    {
    public:
        convert_string(const std::string &_string) : std::string(_string) {}
        convert_string() {}
        
        inline operator short () const { return operator int (); }
        inline operator unsigned short () const { return operator unsigned long (); }    
        inline operator unsigned int () const { return operator unsigned long (); }
        inline operator float () const { return operator long double (); }
        inline operator double () const { return operator long double (); }
        
        operator char () const 
        {
            if(empty()) return '\0';
            return at(0);
        }
    
        operator int () const
        {
            if(empty()) return 0;
            return atoi(c_str());
        }
    
        operator long () const
        {
            if(empty()) return 0;
            return atol(c_str());
        }
    
        operator unsigned long () const
        {
            if(empty()) return 0;
            return strtoul(c_str(), 0, 10);
        }
    
        operator long double () const
        {
            if(empty()) return 0.0;
            return atof(c_str());
        }
    
        operator bool () const
        {
            if(empty()) return false;
            
            // Convert to lowercase
            std::string _lowerString = *this;
            for(std::string::iterator it = _lowerString.begin();it != _lowerString.end();++it)
                *it = tolower(*it);
            
            if(_lowerString == "1" || _lowerString == "true" || _lowerString == "yes" || _lowerString == "ok")        return true;
            
            return false;
        }
    };
}
#endif /* _CONVERT_STRING_H */


Не уверен, что у меня есть полное право вносить его в namespace std, но там он смотрится органично. Стоило бы его наследовать от std::basic_string и сделать соотвествующим шаблоном, конечно, но пока так. Используется очень просто:
Код


double _dVal = std::convert_string("892.017");
bool _flag = std::convert_string("Yes");                // "OK", "true" - любое позитивное выражение
char _ch = std::convert_string("Any");                 // 'A'

namespace MySQL
{
    class Row
    {
    public:
        // Здесь мы полагаемся на оптимизация компилятора для inline функций и можем возвратить 
        // строку по значению, что еще более упрощает синтаксис
        inline std::convert_string operator[](size_t _index)
        {
            // ...
        }
    };
}

typedef enum
{
    enName, 
    enBirthday,
    enDeposit
}
__clientFields;

MySQL::Row &_row = // ...

std::string _name = _row[enName];
time_t _birthday = (unsigend int)_row[enBirthday];
double _deposit = _row[enDeposit];



В последнем примере MySQL client возвращает все значения в виде С строк и очень удобным оказывается возвращать значения в виде convert_string - это позволяет присваивать их непосредственно переменным с базовыми типами, без явной конвертации.

По поводу возврата в inline функции объекта по значению и надежды на оптимизацию компилятора:
1. Нужно убедиться, что компилятор сделает эту функцию inline. В gcc есть соотвествующая опция "-Winline"
2. В крайнем случае можно проверить путем прямого сравнения производительности на тестовых приложениях.


Это сообщение отредактировал(а) Dragon - 10.8.2006, 17:54
PM MAIL ICQ   Вверх
maxzone
Дата 15.8.2006, 13:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 2
Регистрация: 15.8.2006

Репутация: нет
Всего: нет



Наверное Страуструп имел ввиду что-то вроде этого  smile  Это же все-таки с++ :
Код

int stringToInt(const char* str)
{
    if(str==NULL || strlen(str)==0) return 0;// или что хочеться :)
    std::istringstream iss(str);
    int i(0);
    iss>>i;
    return i;
}


Отработает [+/-]Число, Число, Число с дробной частью (отбрасывая дробную). Фунциональность больше этой редко требуется  smile 

Это сообщение отредактировал(а) maxzone - 17.8.2006, 11:51
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0645 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.