Tempus |
Отправлено: 18.05.2005, 11:25 |
|
Не зарегистрирован
|
Господа, не подскажите как сделать мксимально быстро по скорости следующую весчь:
Есть строка AnsiString, разделенная 8 запятыми, нужно получить 9 строк, на которые она разделена.
Сразу оговорюсь, что создать TStrings и задать в нем CommaText не работает, т.к. внутри строк могут быть пробелы. Есть ли способ более быстрый, чем проход по всей базовой строке? |
|
VovaN |
Отправлено: 18.05.2005, 11:37 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 72
|
По скорости не знаю, но сделать можно что-то типа такого:
while(i=str.Pos(","))
{
скопировать в строку str.SubString(0,i);
обрезать строку по i (от начала);
}
|
|
Tempus |
Отправлено: 18.05.2005, 11:46 |
|
Не зарегистрирован
|
За совет спасибо. Можно и не обрезать, а сохранять позицию последней запятой. Правда насчет скорости не уверен — и Pos и Substring очень медленные. |
|
** Rius |
Отправлено: 18.05.2005, 12:37 |
|
Не зарегистрирован
|
Можно обратиться напрямую к буферу данных AnsiString'а (AnsiString::c_str()), да еще и на ассмеблере эту часть написать. Будет быстрее некуда. |
|
Tempus |
Отправлено: 18.05.2005, 12:40 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 14
|
Ну к буферу данных AnsiString понятно как обратиться, а вот с ассмом не знаком |
|
Gedeon |
Отправлено: 18.05.2005, 12:52 |
|
Ветеран
Группа: Модератор
Сообщений: 1742
|
Можно так
CODE |
Memo1->Lines->Clear();
int len = Edit1->Text.Length();
char *Temp = new char(len);
strcpy(Temp,Edit1->Text.c_str());
AnsiString T;
for(int counter=0;counter<len;counter++)
{
if(Temp[counter]==',')
{
Memo1->Lines->Add(T);
T = "";
}
else
{
T += AnsiString(Temp[counter]);
}
}
delete Temp; |
|
|
Tempus |
Отправлено: 18.05.2005, 12:57 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 14
|
Тогда такой вопрос — при += для AnsiString будет по новой выделяться память поб ее буфер? К чему спрашиваю — возможно SubString быстрее.
(Я сейчас в офисе и не могу проверить разные варианты на то какой быстрее) |
|
avc* |
Отправлено: 18.05.2005, 13:48 |
|
Не зарегистрирован
|
А еще можно так
CODE |
AnsiString str("a1,a2,a3,a4");
for (char *cp=str.c_str(); *cp; cp++ )
if(*cp == ',') *cp = '\n';
TStringList *lst = new TStringList;
lst->Text = str;
// здесь работаем с отдельными строками
// проверка ShowMessage(lst->Text);
delete lst;
|
|
|
dvv |
Отправлено: 18.05.2005, 14:45 |
|
Дежурный стрелочник
Группа: Участник
Сообщений: 43
|
Идея хорошая, сам таким пользуюсь, но так делать нельзя!
Дело в том, что .c_str() "возвращает указатель на строку, содержащюю те же символы, что и в AnsiString". Замена символа в c_str() не означает замену в AnsiString.
Предворительно нужно создать в памяти объект так, как говорил Гедеон. Только длину создаваемой строки нужно увеличить на символ:
CODE |
AnsiString str("a1,a2,a3,a4");
int len = str.Length();
char *Temp = new char(len+1);
strcpy(Temp,str.c_str());
for (int i=0;i<len;i++ )
if(Temp[i] == ',') Temp[i] = '\n';
TStringList *lst = new TStringList;
lst->Text = AnsiString(Temp);
. . .
|
lst->String[2] будет содержать "a3".
Что касается скорости , то вышеописанный способ работает намного быстрее чем методы AnsiString-a. Но если нужно увеличить скорость еще выше, то пользуйтесь функциями работы с памятью. Например вместо strcpy используйте memcpy.
Если строка длиной 1-2 кБайта, можно пользоватся методами AnsiString-a и не возиться с char*.
Отредактировано dvv — 18/05/2005, 14:59 |
|
avc* |
Отправлено: 18.05.2005, 15:01 |
|
Не зарегистрирован
|
QUOTE |
Идея хорошая, сам таким пользуюсь, но так делать нельзя!
Дело в том, что .c_str() "возвращает указатель на строку, содержащюю те же символы, что и в AnsiString".
Предворительно нужно создать в памяти объект так, как говорил Гедеон. Только длину создаваемой строки нужно увеличить на символ:
|
Можно. Незачем создавать новую строку. Можно работать в старой так как размер строки не изменяется.
Это не идея, а вполне рабочий код. |
|
Tempus |
Отправлено: 19.05.2005, 10:41 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 14
|
И так, господа, я посмотел некоторые варианты алгоритма, и вот к чему пришел:
1. c_str() действительно возвращает указатель на буфер строки, но корректно работать с ним можно только, если не меняется длина строки — судя по всему длина AnsiString вычисляется не по буферу, а при его заполнении стандартными методами.
2. Наиболее быстрым способом оказался (что меня несколько удивило) следующий:
CODE |
int len=str.Length(),done=0;
for (int i=1; i<len+1; i++)
{
if ((str[i]==',')&&(done<8))
{
str[i]='\n';
done++;
}
}
StrList->Text=str;
|
StrList это указатель на TStringsList
Отредактировано Gedeon — 19/05/2005, 11:39 |
|
Gedeon |
Отправлено: 19.05.2005, 11:45 |
|
Ветеран
Группа: Модератор
Сообщений: 1742
|
2 Tempus
Пользуйтесь тэгами [CODE]
|
|
AVC |
Отправлено: 19.05.2005, 12:16 |
|
Ветеран
Группа: Модератор
Сообщений: 1583
|
QUOTE |
2. Наиболее быстрым способом оказался (что меня несколько удивило) следующий:
|
Меня тоже. (это самый медленный способ).
CODE |
void __fastcall TF_List::Button7Click(TObject *Sender)
{
AnsiString str;
AnsiString prot;
int done;
double dltt;
__int64 qp_freq, qp_begin, qp_end;
QueryPerformanceFrequency ((LARGE_INTEGER*)&qp_freq);
str = "";
for (int i=0; i < 2000; i++) str += AnsiString::StringOfChar('a',100) + ",";
QueryPerformanceCounter ((LARGE_INTEGER*)&qp_begin);
for (char *cp=str.c_str(); *cp; cp++ )
if(*cp == ',') *cp = '\n';
QueryPerformanceCounter((LARGE_INTEGER*)&qp_end);
dltt = (double(qp_end — qp_begin) * double(1000)) / double(qp_freq);
prot += FormatFloat("0.000", dltt) + "\n";
/* 0.567 ----------------------------------------------------- */
str = "";
for (int i=0; i < 2000; i++) str += AnsiString::StringOfChar('a',100) + ",";
int len=str.Length();
done = 0;
QueryPerformanceCounter ((LARGE_INTEGER*)&qp_begin);
for (int i=1; i <= len; i++)
{ if (str[i]==',' && done < 8)
{ str[i]='\n';
done++;
}
}
QueryPerformanceCounter((LARGE_INTEGER*)&qp_end);
dltt = (double(qp_end — qp_begin) * double(1000)) / double(qp_freq);
prot += FormatFloat("0.000", dltt) + "\n";
/* 9.007 ----------------------------------------------------- */
str = "";
for (int i=0; i < 2000; i++) str += AnsiString::StringOfChar('a',100) + ",";
done = 0;
QueryPerformanceCounter ((LARGE_INTEGER*)&qp_begin);
for (char *cp=str.c_str(); *cp; cp++ )
if(*cp == ',')
{ if(++done > 8) break;
*cp = '\n';
}
QueryPerformanceCounter((LARGE_INTEGER*)&qp_end);
dltt = (double(qp_end — qp_begin) * double(1000)) / double(qp_freq);
prot += FormatFloat("0.000", dltt) + "\n";
/* 0.005 ----------------------------------------------------- */
ShowMessage(prot);
}
|
Нужны пояснения?
|
|
Tempus |
Отправлено: 19.05.2005, 13:15 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 14
|
Wow!!!
Спасибо за пример, надеюсь в некомерческих целях использовать Ваш код можно :-)
Я сравнивал другими алгоритмами, не пытаясь оптимизировать этот. К тому же скорость работы проверял "на глаз" — на выборке из 36000 строк. |
|
avc* |
Отправлено: 19.05.2005, 13:26 |
|
Не зарегистрирован
|
Можно и в комерческих, но тогда с упоминанием Форума (кыргуду)
|
|
Gedeon |
Отправлено: 19.05.2005, 15:18 |
|
Ветеран
Группа: Модератор
Сообщений: 1742
|
А зачем считать время в килосекундах?
|
|
Guest |
Отправлено: 19.05.2005, 15:56 |
|
Не зарегистрирован
|
А где сказано, что это в секундах?
Просто для уменьшения числа лидирующих 0. |
|
Gedeon |
Отправлено: 19.05.2005, 16:49 |
|
Ветеран
Группа: Модератор
Сообщений: 1742
|
QUOTE (Guest @ 19/05/2005, 15:56) | А где сказано, что это в секундах?
|
Я — тупой. В милисекундах.
QUOTE |
Просто для уменьшения числа лидирующих 0. |
Согласен, так удобнее.
Отредактировано Gedeon — 19/05/2005, 16:49
|
|
Tempus |
Отправлено: 20.05.2005, 10:41 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 14
|
Еще один вопрос в продолжение темы:
Нужна функция, которая бы сравнивала строки, без учета регистра (так же как AnsiCompareIC). Если с учетом регистра, то можно так:
CODE |
char StrCompare(char *s1, char *s2)
{
char res;
for (s1;(*s1)*(*s2);s1++,s2++)
{
if ((res=*s1-*s2)) return res;
}
return (*s1-*s2);
} |
а как сделать, чтобы без учета регистра?
Отредактировано Tempus — 20/05/2005, 10:42 |
|
Guest |
Отправлено: 20.05.2005, 12:49 |
|
Не зарегистрирован
|
Пример преобразования к верхнему регистру
(ТОЛЬКО для английских и русских)
CODE |
unsigned char *cp;
.........
if ((*cp >= 0x61 && *cp <= 0x7A) || (*cp >= 0xE0 && *cp <= 0xFF)) *cp -= 0x20;
else if (*cp == 0xB8) *cp = 0xA8;
|
|
|
Tempus |
Отправлено: 20.05.2005, 13:06 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 14
|
Спасибо, а часом в stdio.h нет стандартной функции приведения char к верхнему регистру? |
|
Guest |
Отправлено: 20.05.2005, 13:59 |
|
Не зарегистрирован
|
Есть такое
QUOTE |
The CharUpper function converts a character string or a single character to uppercase. If the operand is a character string, the function converts the characters in place. This function supersedes the AnsiUpper function.
LPTSTR CharUpper(
LPTSTR lpsz // single character or pointer to string
);
|
|
|
Gedeon |
Отправлено: 20.05.2005, 14:32 |
|
Ветеран
Группа: Модератор
Сообщений: 1742
|
так а
CODE |
int strcmpi(const char *s1, const char *s2);
int _wcscmpi(const wchar_t *s1, const wchar_t *s2); |
не подходит? пример
CODE |
char *s1 = "Sasha";
char *s2 = "sasha";
int res = strcmp(s1,s2);
printf("%d\n",res);
int res1 = strcmpi(s1,s2);
printf("%d\n",res1);
system("PAUSE");
return 0; | [/QUOTE]
Результат
|
|