Dimon.Ru |
Отправлено: 23.08.2005, 15:58 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 92
|
Доброго времени суток!
опишу свою проблему:
есть у меня функция, которая преобразует строку в массив чисел (строка содержит числа через разделитель — ';').
есть подозрения, что в этой функции что-то не так.
хотя работает она всегда правильно, но иногда после ее работы я вижу Access Violation...
убираю использование функции из программы — ошибка исчезает.
возможно, я где-то напортачил с указателями, но где — не могу понять...
AnsiString string — строка вида "1,2;34;563,896;0;-52;" и т.д.
double* massif — массив чисел, который получается на выходе. например для вышепреведенной строки это будет double mas[5];
CODE | void Razbivka(AnsiString string, double* massif)
{
AnsiString temp;
char **result;
int kolvo, leng, str_len, s,d,a,x,i;
str_len = string.Length();
for(int i=0; i<str_len; i++) if(string.c_str()[i] == '.') string.c_str()[i] = ',';
if (str_len == 0) return;
kolvo = 0; leng=0; x=0;
//Определяем количество искомых подстрок в строке и максимальную длину одной подстроки
for (s=1; s<str_len+1; s++)
{
if (string[s] == ';') //Если разделитель заранее известен и не может меняться
{
kolvo++;
if (x > leng) leng = x;
x = 0;
}
else x++;
}
if (string[str_len] != ';') //Если строка не заканчивается символом-разделителем
{
kolvo++;
if (x > leng) leng = x;
}
//Выделяем память
result = new char *[kolvo];
for (d=0; d<kolvo; d++)
result[d] = new char [leng];
//Разбиваем строку
a=1; //Здесь — нач. позиция нужного идентификатора в строке
x=0; //Здесь — количество символов в идентификаторе
d=0; //Здесь — счетчик переменных
str_len = string.Length();
for (s=1; s<str_len+1; s++)
{
if (string[s] != ';') x++;
else
{
temp = string.SubString(a, x);
strcpy(result[d],temp.c_str());
a=a+x+1; x=0; d++;
}
}
if (string[str_len] != ';') //Если строка не заканчивается символом-разделителем, вычленим еще одну, завершающую подстроку
{
temp = string.SubString(a, x);
strcpy(result[d],temp.c_str());
}
for (d=0; d<kolvo; d++)
{
massif[d]=StrToFloat(result[d]);;
}
for (d=0; d<kolvo; d++)
delete result[d];
delete result;
str_len = 0;
return;
} | |
|
gvg |
Отправлено: 23.08.2005, 16:29 |
|
Машинист паровоза
Группа: Участник
Сообщений: 222
|
Мда, реализация не простая для чтения... А в massif количество элементов где определяется? Вообще, все это переделать надо бы, используя имеющиеся методы AnsiString (напр. Pos). Куда проще выйдет. |
|
Gedeon |
Отправлено: 23.08.2005, 17:21 |
|
Ветеран
Группа: Модератор
Сообщений: 1742
|
CodeGuard хорошая весчь.
|
|
Doga |
Отправлено: 23.08.2005, 19:08 |
|
Мастер участка
Группа: Участник
Сообщений: 575
|
Не парьтесь
*HPP
CODE |
#include <system.hpp>
#include <Classes.hpp>
//---------------------------------------------------------------------------
typedef struct {
double *Values;
bool *Errors;
int Count;
} RESULTSTRUCT;
//---------------------------------------------------------------------------
RESULTSTRUCT * __fastcall StringToValues(AnsiString ValuesString, AnsiString Separator = ";");
//---------------------------------------------------------------------------
|
*.CPP
CODE |
//---------------------------------------------------------------------------
RESULTSTRUCT * __fastcall StringToValues(AnsiString ValuesString, AnsiString Separator)
{
RESULTSTRUCT *Result = NULL;
if ((ValuesString != "") && (Separator != ""))
{
ValuesString = StringReplace(ValuesString, Separator, "\n", TReplaceFlags()<<rfReplaceAll);
TStringList *ValuesList = new TStringList();
ValuesList->Clear();
ValuesList->Text = ValuesString;
for (int k = 0; k < ValuesList->Count; k++)
{
if (ValuesList->Strings[k] == "")
{
ValuesList->Delete(k);
k--;
}
}
if (ValuesList->Count > 0)
{
Result = new RESULTSTRUCT;
Result->Count = ValuesList->Count;
Result->Values = new double[Result->Count];
Result->Errors = new bool[Result->Count];
for (int k = 0; k < ValuesList->Count; k++)
{
try
{
Result->Values[k] = ValuesList->Strings[k].ToDouble();
Result->Errors[k] = false;
}
catch (EConvertError &E)
{
Result->Errors[k] = true;
}
}
}
delete ValuesList;
}
return Result;
}
//---------------------------------------------------------------------------
|
Using
CODE |
RESULTSTRUCT *ValuesStruct = StringToValues(String)
if (ValuesStruct)
{
for (int k = 0; k < ValuesStruct->Count; k++)
{
if (!ValuesStruct->Errors[k])
{
//Можно спрашивать ValuesStruct->Values[k]
}
else
{
//ОШИБКА
}
}
}
else
{
//ОШИБКА
}
//Или
RESULTSTRUCT *ValuesStruct = StringToValues(String, " ")
//...
|
|
|
Admin |
Отправлено: 23.08.2005, 21:00 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Как-то вы все усложнили, буду завтра днем на работе,
постараюсь найти время ответить, все это делается
намного проще, там у AnsiString и соответствующие
методы по работе с разделителями насколько я помню есть.
|
|
Георгий |
Отправлено: 23.08.2005, 21:22 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
голову за такой код отрывать надо.
когда то считалось что достаточно написать программу что бы она работала... сейчас это уже не так — программу надо писать так, что бы она была легко модернизируемой.
PS. всё что выше написано о первом коде в этой ветке.
Отредактировано Георгий — 24/08/2005, 08:31 |
|
Doga |
Отправлено: 23.08.2005, 23:02 |
|
Мастер участка
Группа: Участник
Сообщений: 575
|
2Георгий
Всегда приятно слышать добрые слова в свой адрес
По крайней мере Вы признаёте, что этот код работает.
Приведёный мной код из неболшй утилитки, где было важно знать результат преобразования по каждой подстроке. Я подумал, мож Димону тоже это пригодится...
Никто не запрещает ему переписать всё это в ввиде, ну например:
double *StringToValues(int &Count, AnsiString ValuesString, AnsiString Separator);
А ипользование в коде TStringList и StringReplace не напрягает ни сам процессор ни его озу. Тем более меня. А таких компов, которых это напрягает, в природе уже не почти не существует
Если Вы уж решили наводить критику, то, будьте так любезны, поподробнее о том что Вам не нравится. Не совсем понятно, причём здесь легкость модернизации?
Отредактировано Doga — 23/08/2005, 23:03
|
|
Rius |
Отправлено: 24.08.2005, 07:01 |
|
Мастер участка
Группа: Участник
Сообщений: 321
|
Вот похожая функция, для получения элементов строки вида "1.2.5.870":
CODE | bool __fastcall TPlugin::ExtractFileVersions(AnsiString Vers, WORD * Major, WORD * Minor, WORD * Release, WORD * Build)
{
try
{
int i1, i2;
AnsiString str = Vers;
i1 = 1;
i2 = str.AnsiPos(".");
*Major = str.SubString(i1, i2-i1).ToInt();
str.Delete(i1, i2-i1+1);
i2 = str.AnsiPos(".");
*Minor = str.SubString(i1, i2-i1).ToInt();
str.Delete(i1, i2-i1+1);
i2 = str.AnsiPos(".");
*Release = str.SubString(i1, i2-i1).ToInt();
str.Delete(i1, i2-i1+1);
// i2 = str.AnsiPos(".");
// i3 = str.Length();
*Build = str.ToInt();
// i2 = str.SubString(i2+1, i3-i2-1);
}
catch(...)
{
// Application->MessageBox(AnsiString("Ошибка при считывании версии файла " + Vers).c_str(), "Внимание",MB_ICONEXCLAMATION|MB_OK);
return false;
}
return true;
} |
|
|
Георгий |
Отправлено: 24.08.2005, 07:41 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
QUOTE (Doga @ 24/08/2005, 00:02) | 2Георгий
Если Вы уж решили наводить критику, то, будьте так любезны, поподробнее о том что Вам не нравится. Не совсем понятно, причём здесь легкость модернизации? |
я о dimon`e говорил
твой код несколько лучше, чем Dimon`а, но всё равно содержит жуткие вещи:
возврат массива в виде double* или массива структур как у тебя.
почему бы не использовать банальный std::vector или std::vector и описать RESULTSTRUCT не как 2 синхронных массива, а какCODE | typedef struct {
double Values;
bool Errors;
} RESULTSTRUCT; |
так проще? проще. проблем с освобождением/выделением памяти нет? нет. с выходом за индексы тоже проблем нет? нет.
осталось твою функцию подправить, что бы утечек памяти не было в случае возникновения исключений.CODE | auto_ptr<TStringList>ValuesList(new TStringList()); |
в результате получаем код, в который кто угодно, какие угодно изменения внесёт и программы не вылетит по AE и не будет память течь.
Отредактировано Георгий — 24/08/2005, 08:46 |
|
olegenty |
Отправлено: 24.08.2005, 08:08 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
у auto_ptr есть только один недостаток — его содержимое хрен куда отдашь как параметр (нативного типа).
|
|
gvg |
Отправлено: 24.08.2005, 08:36 |
|
Машинист паровоза
Группа: Участник
Сообщений: 222
|
А вот еще одна функция для разбивки строк:
//---------------------------------------------------------------------------
// если mode — убираем повторяющиеся разделители
void __fastcall ParseStr ( TStrArr &sa, AnsiString Value, char sep, bool mode )
{
char *s;
AnsiString str;
Value = Value.Trim ();
s = Value.c_str ();
sa.Clear ();
for ( int i = 0; s[i]; i++ )
{
if ( mode && s[ i + 1 ] && s[i] == sep && s[ i + 1 ] == sep )
continue;
if ( s[i] == sep )
{
sa.Add ( str );
str = "";
}
else
{
str += s[i];
}
}
if ( !str.IsEmpty ())
sa.Add ( str );
}
//---------------------------------------------------------------------------
Только заменить TStrArr на TStringList. Здесь sep — символ разделителя, при mode = true игнорируются пустые строки между разделителями.
Использую лет 5 уже как...
|
|
Admin |
Отправлено: 24.08.2005, 09:31 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
QUOTE |
AnsiString string — строка вида "1.2;34;563.896;0;-52;" и т.д.
double* massif — массив чисел, который получается на выходе.
например для вышепреведенной строки это будет double mas[5];
|
Ну вот, добрался до работы...
Во первых, где-то есть функция такой разбивки
у AnsiString, название не помню ищите в help-e
---
Вот так еще проще(может и есть какие ошибки проверяйте сами):
CODE |
using namespace std;
#include <vector>
void __fastcall TForm1::Button1Click(TObject *Sender)
{
vector <double> massif;
AnsiString s = Edit1->Text; // "1.2;34;563.896;0;-52;"
while(int ld = s.Pos(";")){
massif.push_back(s.SubString(1, ld-1).ToDouble());
s.Delete(1, ld);
}
for(int i=0; i<massif.size(); i++) ShowMessage(massif[i]);
massif.clear(); // очищаем массив, освобождаем память когда стал не нужен.
}
//---------------------------------------------------------------------------
|
|
|
Doga |
Отправлено: 24.08.2005, 13:33 |
|
Мастер участка
Группа: Участник
Сообщений: 575
|
2Георгий
QUOTE |
но всё равно содержит жуткие вещи
|
Вы просто не знаете для чего использовалась эта функция.
В структуре было ещё 2 поля — список подстрок и список типов подстрок, т.е. double, hex и др. Были ещё и аналогичные функции для конвертации в остальные типы. А массивы должны были использоваться в дальнейшем именно в том виде как они объявлены в структуре. Все данные для использования результирующих массивов имеются, так что только младенец может допустить утечку памяти.
А вместо std::vector предпочитаю использовать TList с соответствующими типизированными оболочками.
|
|
Георгий |
Отправлено: 24.08.2005, 20:18 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
всё равно — синхронные массивы это зло вселенского масштаба.
код надо писать вот такой, как Владимир привёл — коротко, ясно, ошибиться просто негде. единственно у него обработка завершается после считывания последней ";", но это исправляетя добавлением ещё 2 функциональных строчекCODE | if(s[s.Length()]!=";")s+=";"; | и всё — получена функция в 10 строчек! сравните это с исходной функцией, в которой одних только циклов 5 штук!!!
Отредактировано Георгий — 24/08/2005, 21:27 |
|
olegenty |
Отправлено: 25.08.2005, 14:51 |
|
Ветеран
Группа: Модератор
Сообщений: 2412
|
QUOTE (Георгий @ 24/08/2005, 21:18) | код надо писать вот такой, как Владимир привёл — коротко, ясно, ошибиться просто негде. единственно у него обработка завершается после считывания последней ";", но это исправляетя добавлением ещё 2 функциональных строчекCODE | if(s[s.Length()]!=";")s+=";"; |
|
2 Георгий
угу, только в реальной жизни ещё убедиться, что не IsEmpty(), иначе приведённый тобой код по идее приводит к AV.
|
|
Admin |
Отправлено: 25.08.2005, 15:29 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
Ну в реальной жизни еще надо выполнять проверку
на то что в строке, иначе и в преобразовании .ToDouble();
получим AV EConvertError.
P.S. Ну ведь самое обидное — есть такая "родная" функция в Builder
видел ее, даже раз пользовался, но не могу найти !
Отредактировано Admin — 25/08/2005, 15:32
|
|
avc* |
Отправлено: 25.08.2005, 15:35 |
|
Не зарегистрирован
|
IsEmpty мало. Нужна функция типа AnsiString::ToDoubleDef(double df_value); |
|
Admin |
Отправлено: 25.08.2005, 15:38 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
to avc:
не успели на 6 минут
|
|
avc* |
Отправлено: 25.08.2005, 17:05 |
|
Не зарегистрирован
|
Ага. опоздал. INet второй день глючит, то "нэма" то "нэту". Провайдер что-то перенастраивает. |
|
Admin |
Отправлено: 25.08.2005, 18:59 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
QUOTE | Нужна функция типа AnsiString::ToDoubleDef(double df_value); |
Такой функции нету. Есть аналогичная StrToFloatDef()
а что искал так и не нашел, нашел похожую — ExtractStrings(),
но у той было еще более длинное название.
Тогда можно написать и так:
CODE |
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
double errValue = -1000000; // ошибка при преобразовании
AnsiString s = "195.2;11;154.254;-52;10;-544;1451;28";
TStringList *SL = new TStringList;
TSysCharSet Delimiters; Delimiters<<';';
int n = ExtractStrings(Delimiters, Delimiters, s.c_str(), SL);
if(n){
double *massif = new double[n];
for(int i=0; i<n; i++) {
massif[i]= StrToFloatDef(SL->Strings[i], errValue);
ShowMessage(massif[i]);
}
delete []massif;
}
delete SL;
}
//---------------------------------------------------------------------------
|
|
|
Doga |
Отправлено: 26.08.2005, 12:51 |
|
Мастер участка
Группа: Участник
Сообщений: 575
|
А если значение какой либо подстроки случайно совпвдет с errValue?
Тогда уж лучше его не добавлять в массив... Или всё таки использовать флаги ошибок
Отредактировано Doga — 26/08/2005, 12:51
|
|
AVC |
Отправлено: 26.08.2005, 13:03 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
QUOTE |
А если значение какой либо подстроки случайно совпвдет с errValue?
|
Это не возможно, так как errValue это значение "по умолчанию" и используется если строка не переводима в число. Другой вопрос, нужны ли DF значения заказчику? Но это зависит от логики программы.
2Admin
А у меня и StrToFloatDef нет (по крайней мере в Help не упоминается). А AnsisToDoubleDef давным давно написал и включил в одну из библиотек нижнего уровня. |
|
Dimon.Ru |
Отправлено: 30.08.2005, 16:05 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 92
|
Спасибо всем за советы. на днях раскопал в одной очень старенькой своей проге решение своего вопроса....
всего несколько строк, но вопрос решился быстро!!!
приду домой — выложу код. |
|
Dimon.Ru |
Отправлено: 31.08.2005, 16:44 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 92
|
вотъ:
CODE |
bool Razbivka(AnsiString stroka, double* massif)
{// Разбивка строки на элементы массива
AnsiString* itogMass; //Массив
int quanValues=0; //количество переменных
char *buff;
char *vhod;
int k,str_len=0;
buff=stroka.c_str();
//Определяем длину строки
str_len = stroka.Length();
if(str_len==0) return false;
// Определяем количество переменных
for(int i=0; i<str_len; i++)
if(stroka.c_str()[i] == ';') quanValues++;
if(quanValues<2) return false;
// Выделяем память под строки
itogMass = new AnsiString[quanValues];
for(int i=0;i<quanValues;i++)
{
if((vhod=strchr(buff,';'))==NULL) break;
k=(int)(vhod-buff);
char TEMP[100];
strncpy(TEMP,buff,k);
TEMP[k]='\0';
itogMass[i]=TEMP;
massif[i]=StrToFloat(itogMass[i]);
buff+=(k+1);
}
// Удалим массив
delete[] itogMass;
return true;
}
|
а double* massif объявляется до вызова функции Razbivka()
зы: может и коряво, но работает.
Отредактировано Dimon.Ru — 31/08/2005, 16:45 |
|
Admin |
Отправлено: 31.08.2005, 16:51 |
|
Владимир
Группа: Администратор
Сообщений: 1190
|
А каким образом double * massif объявляется до вызова функции Razbivka() ???
Если вы заранее не знаете сколько в этом массиве будет
элементов — где выделение памяти под этот массив ?
Размер массива — 1 , 100, 1000, 10000 элементов ???
(остальной код не смотрел)
|
|
Asher |
Отправлено: 31.08.2005, 17:59 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
Привет.
Если тема еще интерсна, то есть еще вариант.
CODE |
AnsiString s = "";
TStringList* psl = new TStringList();
psl->Delimiter = ';';
psl->DelimitedText = "195.2;11;154.254;-52;10;-544;1451;28";
int itEnd = psl->Count;
for(int itPos = 0; itPos < itEnd; ++itPos)//
s = psl->Strings[itPos]; |
Все само работает.
Из минусов — завязка на VCL
P.S.
Отредактировано Asher — 31/08/2005, 19:00
|
|
gvg |
Отправлено: 01.09.2005, 08:01 |
|
Машинист паровоза
Группа: Участник
Сообщений: 222
|
QUOTE (Asher @ 31/08/2005, 17:59) | Привет.
Если тема еще интерсна, то есть еще вариант.
CODE |
AnsiString s = "";
TStringList* psl = new TStringList();
psl->Delimiter = ';';
psl->DelimitedText = "195.2;11;154.254;-52;10;-544;1451;28";
int itEnd = psl->Count;
for(int itPos = 0; itPos < itEnd; ++itPos)//
s = psl->Strings[itPos]; |
Все само работает.
Из минусов — завязка на VCL
P.S. <tpjgfcyjt преобразование в численные значения — отдельная тема |
Смотрится красиво, но откуда
psl->Delimiter ?
psl->DelimitedText ?
|
|
Asher |
Отправлено: 01.09.2005, 08:16 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
В смысле откуда?
Унаследованы TStringList от своего предка TStrings
Во всяком случае в Builder6.
Пятый давно не стоит, проверить не могу.
|
|
gvg |
Отправлено: 01.09.2005, 08:24 |
|
Машинист паровоза
Группа: Участник
Сообщений: 222
|
Понял, у меня пятый. |
|
Dimon.Ru |
Отправлено: 01.09.2005, 13:10 |
|
Станционный диспетчер
Группа: Участник
Сообщений: 92
|
QUOTE (Admin @ 31/08/2005, 16:51) | А каким образом double * massif объявляется до вызова функции Razbivka() ???
Если вы заранее не знаете сколько в этом массиве будет
элементов — где выделение памяти под этот массив ?
Размер массива — 1 , 100, 1000, 10000 элементов ???
(остальной код не смотрел) |
массив объявляется до вызова либо как massif[]
Либо *massif, а потом выделяется память (до вызова функции).
в моём конкретном случае так удобнее.
вот заодно может быть посоветуете как сделать, что бы память выделялась в этой функции?
как ее в этом случае освобождать? |
|