Как выделить число из сложносоставной строки? - страница 8

 
Renat Akhtyamov #:

попробуйте такую

мне кажется что строкой надо поиграться, выяснить в чем причина для начала 

ну и тут у Вас несколько чисел

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

Ваш путь все таки посоветованный мной: сначала массив, потом обработка.

Количество и тип чисел в строке неизвестны. Меня интересует даже не столько результат, сколько понимание, как проводить парсинг с такой постановкой задачи (сам принцип). Узнал много нового для себя в этой теме, но смысл последней и самой универсальной (из рассмотренных здесь) функции пока за пределами моего понимания.

 
leon_17 #:

Количество и тип чисел в строке неизвестны. Меня интересует даже не столько результат, сколько понимание, как проводить парсинг с такой постановкой задачи (сам принцип). Узнал много нового для себя в этой теме, но смысл последней и самой универсальной (из рассмотренных здесь) функции пока за пределами моего понимания.

в этом случае лучше пользоваться тем, что понимаете

в нашем деле всегда что то приходиться шаманить под себя

 
JRandomTrader #:

И такую: "где дед.беда" ))

Ошибка есть даже с такой строкой:

   string row = "1";
 

Немножко переработал, и, надеюсь, упростил понимание .

bool GetNextNumber(string &s, int &i, double &d, bool sp_in_num) {
   const int Size = StringLen(s) - 1;
   uint c = s[i]; 
   bool minus = (c=='-');
   bool period = false;
   bool is_not_null = false;
   long mantissa = 0;
   double p = 1;
   if(minus) i++;
   for(;i<Size;i++) {
      c = uchar(s[i]);
      if(c==' ') {
         if(sp_in_num)
            continue;
         else
            break;
      } else if(c=='.') {
         if(period)
            break;
         else
            period=true;
      } else if (c<='9'&& c>='0') {
         is_not_null = true;
         mantissa = mantissa*10 + c - '0';
         if(period)
            p*=10;
      } else break;
   }

   if (is_not_null) {
      d = (double)mantissa;
      if (minus) d = - d;
      d/=p;
      i--;
      return true;
   }
   if(period)i--;
   return false;
}

int GetDoubleArrayFromString(string Str, double &d[], bool sp_in_num=false) {
   const int Size = StringLen(Str) - 1;
   double Res = 0;
   int size = 0;
   uint uitmp = 0;

   for (int i = 0; i <= Size; i++) {
      uitmp=Str[i];
      if(uitmp>'9' || uitmp<'-' || uitmp=='/')continue;
      if (GetNextNumber(Str,i,Res,sp_in_num)) {
         size++;
         ArrayResize(d,size);
         d[size-1] = Res;
      }
   }
   return(size);
}

sp_in_num - могут ли внутри числа содержаться пробелы (как я понимаю, в исходной версии - могли).

 

и мои 5 копеек :-)

на скорость не претендует, претендует на reusable 

// классификация символов
bool IsNum(ushort ch)
{
   return ch>='0' && ch<='9';
}
bool IsDot(ushort ch) 
{
   return ch=='.' || ch==',';
}
bool IsSign(ushort ch)
{
   return ch=='-' || ch=='+';
}
bool IsAlpha(ushort ch)
{
   return (ch>='a' && ch<='z') || (ch>='A' && ch<='Z') || ch=='_';
}
bool IsAlnum(ushort ch)
{
   return IsAlpha(ch)||IsNum(ch);
}

// длина токенов

/// длина токена "десятичный integer"
/// @arg s         разбираемая строка
/// @arg withSign  разрешить знак +- в начале
/// @arg from      с которой позиции
/// @arg to        по которую позицию
/// @return длина токена в символах, или 0 если токен не integer
int ToklenDecimal(string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   int total=StringLen(s);
   if (from<0) {
      from=total+from;
      if (from<0) return 0;
   }
   if (to==WHOLE_ARRAY || to>total) {
      to=total;
   } else if (to<0) {
      to=total+to;
      if (to<0) return 0;
   }
   if (from>=to) return 0;
   int len=0;
   int pos=from;
   if (withSign && pos+1<to && IsSign(s[pos]) && IsNum(s[pos+1])) {
      len+=2;
      pos+=2;
   }
   while(pos<to && s[pos]>='0' && s[pos]<='9') {
      pos++;
      len++;
   }
   return len;
}
/// длина токена double
/// аналогично ToklenDecimal
int ToklenDouble(string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   int total=StringLen(s);
   if (from<0) {
      from=total+from;
      if (from<0) return 0;
   }
   if (to==WHOLE_ARRAY || to>total) {
      to=total;
   } else if (to<0) {
      to=total+to;
      if (to<0) return 0;
   }
   if (from>=to) return 0;
   int pos=from;
   if (withSign && pos+2<to && IsSign(s[pos]) && IsDot(s[pos+1])) {
      if (IsNum(s[pos+2])) pos++;
      else return 0;
   }
   if (!IsDot(s[pos])) {
      pos+=ToklenDecimal(s,withSign,from,to);
      if (pos>=to) return pos-from;
   }
   if (pos+1<to && IsDot(s[pos]) && IsNum(s[pos+1])) {
      pos+=2;
      pos+=ToklenDecimal(s,false,pos,to);
   }
   if ((pos+1<to && (s[pos]=='e'||s[pos]=='E') && IsNum(s[pos+1])) ||
      (pos+2<to && (s[pos]=='e'||s[pos]=='E') && IsSign(s[pos+1]) && IsNum(s[pos+2])) ) {
      pos++;
      pos+=ToklenDecimal(s,true,pos,to);
   }
   return pos-from;
}
/// длина токена "идентификатор С"
/// аналогично ToklenDecimal
int ToklenIdent(string &s,bool ignoreThis=true,int from=0,int to=WHOLE_ARRAY)
{
   int total=StringLen(s);
   if (from<0) {
      from=total+from;
      if (from<0) return 0;
   }
   if (to==WHOLE_ARRAY || to>total) {
      to=total;
   } else if (to<0) {
      to=total+to;
      if (to<0) return 0;
   }
   if (from>=to) return 0;
   int pos=from;
   if (!IsAlpha(s[pos])) return 0;
   while(pos<to && IsAlnum(s[pos]))
      pos++;
   return pos-from;
}
/// длина токена string или char
/// аналогично ToklenDecimal
int ToklenString(string &s,bool ignoreThis=true,int from=0,int to=WHOLE_ARRAY)
{
   int total=StringLen(s);
   if (from<0) {
      from=total+from;
      if (from<0) return 0;
   }
   if (to==WHOLE_ARRAY || to>total) {
      to=total;
   } else if (to<0) {
      to=total+to;
      if (to<0) return 0;
   }
   if (from>=to) return 0;
   int pos=from;
   if (pos+2>=to) return 0;   
   if (s[from]!='\"' && s[from]!='\'') return 0;
   pos++;
   while(pos<to) {
      if (pos=='\\') {
         // escapes, to be continued...
         if (pos+1>=to) return 0;
      } else if (s[pos]==s[from]) {
         return pos-from+1;
      }
      pos++;
   }
   return 0;
}
int ToklenDoubleOrIdent(string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   int toklen=ToklenDouble(s,withSign,from,to);
   if (toklen==0) {
      toklen=ToklenIdent(s,withSign,from,to);
   }
   return toklen;
}
int ToklenAny(string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   int toklen=ToklenDouble(s,withSign,from,to);
   if (toklen==0) {
      toklen=ToklenIdent(s,withSign,from,to);
   }
   if (toklen==0) {
      toklen=ToklenString(s,withSign,from,to);
   }
   return toklen;
}
/// хелпер - добавить элемент к массиву
bool ArrayPush(string &arr[],const string s)
{
   int pos=ArraySize(arr);
   if (ArrayResize(arr,pos+1)!=pos+1) {
      return false;
   }   
   arr[pos]=s;
   return true;
}

typedef int (*TokenLengthCB)(string &,bool,int,int);
int ExtractTokens(TokenLengthCB tokenLength,string &arr[],string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   if (tokenLength==NULL) return 0;
   int total=StringLen(s);
   if (from<0) {
      from=total+from;
      if (from<0) return 0;
   }
   if (to==WHOLE_ARRAY || to>total) {
      to=total;
   } else if (to<0) {
      to=total+to;
      if (to<0) return 0;
   }
   if (from>=to) return 0;
   int pos=from;
   while(pos<to) {
      int toklen=tokenLength(s,withSign,pos,to);
      if (toklen) {
         string value=StringSubstr(s,pos,toklen);
         ArrayPush(arr,value);
         pos+=toklen;
         continue;
      } 
      pos++;
   }
   return ArraySize(arr);   
}
int ExtractDoubles(string &arr[],string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   return ExtractTokens(ToklenDouble,arr,s,withSign,from,to);
}
int ExtractAny(string &arr[],string &s,bool withSign=true,int from=0,int to=WHOLE_ARRAY)
{
   return ExtractTokens(ToklenAny,arr,s,withSign,from,to);
}
void TestExtract()
{
   string test = "test-test box 1.23456 \"impossible m\" +"; 
   string values[];
   int count=ExtractAny(values,test,true);
   for(int t=0;t<count;t++) {
      PrintFormat("extracted value %d = %s",t,values[t]);
   }
}
void OnStart()
{
   TestExtract();
}

а то вдруг понадобиться вытаскивать из строк не только double :-)

 
JRandomTrader #:

Немножко переработал, и, надеюсь, упростил понимание .

sp_in_num - могут ли внутри числа содержаться пробелы (как я понимаю, в исходной версии - могли).

Спасибо! Выглядит и правда понятнее! Завтра на свежую голову буду тестировать и наверное уже буду пытаться разбирать по полочкам.

Maxim Kuznetsov #:
reusable

Спасибо! Надеюсь всё это не только мне )

 
JRandomTrader #:

Немножко переработал, и, надеюсь, упростил понимание .

sp_in_num - могут ли внутри числа содержаться пробелы (как я понимаю, в исходной версии - могли).

Потестировал сейчас все-таки. Распознается только первое число и только в случае, если за ним что-то есть.

 
leon_17 #:

Потестировал сейчас все-таки. Распознается только первое число и только в случае, если за ним что-то есть.

bool GetNextNumber(string &s, int &i, double &d, bool sp_in_num) {
   const int Size = StringLen(s);
   uint c = s[i]; 
   bool minus = (c=='-');
   bool period = false;
   bool is_not_null = false;
   long mantissa = 0;
   double p = 1;
   if(minus) i++;
   for(;i<Size;i++) {
      c = uchar(s[i]);
      if (c<='9'&& c>='0') {
         is_not_null = true;
         mantissa = mantissa*10 + c - '0';
         if(period)
            p*=10;
      } else if(c=='.') {
         if(period)
            break;
         else
            period=true;
      } else if(c==' ') {
         if(sp_in_num)
            continue;
         else
            break;
      } else break;
   }
   if (is_not_null) {
      d = (double)mantissa;
      if (minus) d = - d;
      d/=p;
      i--;
      return true;
   }
   if(period)i--;
   return false;
}

int GetDoubleArrayFromString(string Str, double &d[], bool sp_in_num=false) {
   const int Size = StringLen(Str) - 1;
   double Res = 0;
   int size = 0;
   uint uitmp = 0;

   for (int i = 0; i <= Size; i++) {
      uitmp=Str[i];
      if(uitmp>'9' || uitmp<'-' || uitmp=='/')continue;
      if (GetNextNumber(Str,i,Res,sp_in_num)) {
         size++;
         ArrayResize(d,size);
         d[size-1] = Res;
      }
   }
   return(size);
}

Так должно быть правильнее и, в среднем, быстрее.

 
JRandomTrader #:

Так должно быть правильнее и, в среднем, быстрее.

Вроде работает! Но реальные тесты реально лучше отложу пока голова свежей не будет. Насчет того, что распознается только первое число в своем предыдущем сообщении (и возможно и насчет остального) - это я затупил с выводом значений. Завтра автоматизирую это. Еще раз спасибо!


 
JRandomTrader #:

Так должно быть правильнее и, в среднем, быстрее.

норм. Спасибо!