Visual Basic, .NET, ASP, VBScript
 

   
   
     

Форум - Олимпиады

Страница: 1 | 2 | 3 | 4 | 5 |

 

  Вопрос: VC++ Help Please ! (задачка) Добавлено: 08.11.05 23:06  

Автор вопроса:  HACKER

Ответить

  Ответы Всего ответов: 67  

Номер ответа: 46
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #46 Добавлено: 14.11.05 09:59
//==========================================================
char* replace ( char* lpString, char* lpMatch, char* lpReplace )
{
    char    buffer[128];
    int     match_count=0;
    int     str_len = strlen(lpString);
    int     match_len = strlen(lpMatch);
    int     repl_len = strlen(lpReplace);
    int     required;
    char*   pNext;
    char*   pDest;
    
    pNext = lpString;
    /* считаем кол-во вхождений подстроки lpMatch в lpString */
    while ( (pNext = strstr(pNext,lpMatch)) != NULL )
    {
        match_count++;  /* увеличиваем счётчик */
        pNext++;        /* смещаемся на найденую позицию */
    }
    /* если нет вхождений - возвращаем ноль */
    if ( ! match_count ) return 0;
    /* определение длины выходной строки */
    required = str_len - match_len*match_count + repl_len*match_count;
    /* выделение места для вых. строки и её обнуление */
    char* output = new char[required];
    output[0]=0;
    
    
    pNext = lpString;
    /* пока в строке будут встречаться lpMatch, будем заменять
       на lpReplace, сдвигаясь после каждой замены по строке */
    do
    {
        pDest = strstr(pNext,lpMatch);
        /* если все замены сделаны - выходим */
        if ( ! pDest ) break;        
        /* копируем в вых. строку фрагмент до lpMatch */
        strncat(output,pNext,(pDest-pNext));
        /* вместо lpMatch цепляем lpReplace */
        strcat(output,lpReplace);
        pNext = pDest+match_len;
    }
    while (1);
    /* дописываем в вых. строку конец строки */
    strcat(output,pNext);
    
    /* возвращаем указатель на получившуюся строку */    
    return output;

}
Количество переменных можно подсократить, но чтобы понятней было, оставил как есть.
Вызывать примерно так:

    char* new_string = replace ("My cool string", "My", "Your";);
    printf ("%s\n", new_string);
    delete[] new_string;
    cin >>buffer;

Возвращаемую строку, когда она не нужна больше, нужно освободить при помощи delete[]

Ответить

Номер ответа: 47
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #47 Добавлено: 14.11.05 10:29
Спасибо!
У меня ящиков пива нехватит чтобы тебя отблагодарить!

Ответить

Номер ответа: 48
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #48 Добавлено: 14.11.05 10:49
хм...


#include <windows.h>
    #include <iostream.h>



//==========================================================
char* replace ( char* lpString, char* lpMatch, char* lpReplace )
{
    char    buffer[128];
    int     match_count=0;
    int     str_len = strlen(lpString);
    int     match_len = strlen(lpMatch);
    int     repl_len = strlen(lpReplace);
    int     required;
    char*   pNext;
    char*   pDest;
     
    pNext = lpString;
    /* считаем кол-во вхождений подстроки lpMatch в lpString */
    while ( (pNext = strstr(pNext,lpMatch)) != NULL )
    {
        match_count++;  /* увеличиваем счётчик */
        pNext++;        /* смещаемся на найденую позицию */
    }
    /* если нет вхождений - возвращаем ноль */
    if ( ! match_count ) return 0;
    /* определение длины выходной строки */
    required = str_len - match_len*match_count + repl_len*match_count;
    /* выделение места для вых. строки и её обнуление */
    char* output = new char[required];
    output[0]=0;    
     
    pNext = lpString;
    /* пока в строке будут встречаться lpMatch, будем заменять
       на lpReplace, сдвигаясь после каждой замены по строке */
    do
    {
        pDest = strstr(pNext,lpMatch);
        /* если все замены сделаны - выходим */
        if ( ! pDest ) break;        
        /* копируем в вых. строку фрагмент до lpMatch */
        strncat(output,pNext,(pDest-pNext));
        /* вместо lpMatch цепляем lpReplace */
        strcat(output,lpReplace);
        pNext = pDest+match_len;
    }
    while (1);
    /* дописываем в вых. строку конец строки */
    strcat(output,pNext);
     
    /* возвращаем указатель на получившуюся строку */    
    return output;

}

void main(){

    char* new_string = replace ("My cool string", "My", "Your";);
    //printf ("%s\n", new_string);
cout<<new_string;
    delete[] new_string;
}


страшным страшным матом на меня ругается :)

Debug Error!

DAMAGE after normal block (#42) at 0x00431CE0

Ответить

Номер ответа: 49
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #49 Добавлено: 14.11.05 23:35
кста, без
delete[] new_string;

работает... пока так и оставил...

Ответить

Номер ответа: 50
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #50 Добавлено: 15.11.05 05:40
Ну оставлять без освобождения нехорошо, особенно если вызывать большое кол-во раз. Память надо освобождать.

Не знаю, почему Debug Error. У меня консольная vc++ 2003 и ничего не ругается. Может ты debug версию компилишь, и стоит попробовать release?
А вообще можно и по другому сделать:
вместо char* output = new char[required]; поставить
char* output = (char*) SysAllocStringByteLen(NULL,required);
А вместо delete[] new_string
SysFreeString( (BSTR) new_string );
Возможно надо будет указать линкеру либу, где эти ф-ции хранятся:

    #pragma comment(lib, "oleaut32.lib";)

    #include <windows.h>
    #include <iostream>

Ответить

Номер ответа: 51
Автор ответа:
 Sharp


Лидер форума

ICQ: 216865379 

Вопросов: 106
Ответов: 9979
 Web-сайт: sharpc.livejournal.com
 Профиль | | #51
Добавлено: 15.11.05 05:44
Лажа здесь:
char* output = new char[required];

Надо выделять место еще и под завершающий \0:
char* output = new char[required+1];

Кстати, char buffer[128]; нафиг не нужен

Ответить

Номер ответа: 52
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #52 Добавлено: 15.11.05 06:12
Тогда два вопроса:
1. Почему strcat может успешно записать \0 в невыделенное место?
2. Почему у меня не ругается?

P.S.
О необходимости убрать переменные я уже намекал HACKER'у, но он видимо ленивый :) buffer[128] использовался во время отладки

Ответить

Номер ответа: 53
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #53 Добавлено: 15.11.05 06:33
Кстати, созрел ещё один вопрос:
Если выделить например 10 байт, и начиная с адреса полученного от new, записать например 11 байт, то с чего это вдруг delete будет ориентироваться на число байт, которые я записал в этот кусок памяти?
При освобождении освобождается столько, сколько выделено, а не сколько записано. Или не так?

Ответить

Номер ответа: 54
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #54 Добавлено: 15.11.05 11:48
Надо выделять место еще и под завершающий \0:

char* output = new char[required+1];


пасибо, помогло...

buffer убрал, тоже всё ок

щас другая траба с вводом своей строки, вообщем вот всё что получилось:

/*
Задание: Удалить в тексте лишние пробелы

  Реализация функции Replace !
  
*/
#include <windows.h>
#include <iostream.h>

//==========================================================
char* replace ( char* lpString, char* lpMatch, char* lpReplace )
{
//    char    buffer[128];
    int     match_count=0;
    int     str_len = strlen(lpString);
    int     match_len = strlen(lpMatch);
    int     repl_len = strlen(lpReplace);
    int     required;
    char*   pNext;
    char*   pDest;
     
    pNext = lpString;
    /* считаем кол-во вхождений подстроки lpMatch в lpString */
    while ( (pNext = strstr(pNext,lpMatch)) != NULL )
    {
        match_count++;  /* увеличиваем счётчик */
        pNext++;        /* смещаемся на найденую позицию */
    }  

// если нет вхождений - возвращаем ноль
    if ( ! match_count ) return 0;
    // определение длины выходной строки
    required = str_len - match_len*match_count + repl_len*match_count;
    // выделение места для вых. строки и её обнуление
char* output = new char[required+1];
    output[0]=0;    
     
    pNext = lpString;
    // пока в строке будут встречаться lpMatch, будем заменять
    // на lpReplace, сдвигаясь после каждой замены по строке  
    do
    {
        pDest = strstr(pNext,lpMatch);
        // если все замены сделаны - выходим
        if ( ! pDest ) break;        
        // копируем в вых. строку фрагмент до lpMatch
        strncat(output,pNext,(pDest-pNext));
        // вместо lpMatch цепляем lpReplace
        strcat(output,lpReplace);
        pNext = pDest+match_len;
    }
    while (1);
    // дописываем в вых. строку конец строки
    strcat(output,pNext);      
    // возвращаем указатель на получившуюся строку    
return output;
}

void main(){
int str_len_posle; //счётчик длинны строки до изменений
int str_len_do;    //и после изменений

char* new_string="sadasd            sadasd f";    //наша строка

   
do //цкикл пока до и после не будут равны...
{
str_len_do = strlen(new_string) - 1;          //запоминаем
new_string = replace (new_string, "  ", " ";); //убираем лишние пробелы
str_len_posle = strlen(new_string);           //запоминаем
}
//Когда кол-во символов ДО изминений равно кол-ву символу ПОСЛЕ изминений
//ЗНАЧИТ изминения сделаны напрасно :) Но за то мы знаем когда выйти из цикла :))
//Всмысле изменений небыло, т.к. все нужные изменения уже сделаны :)

while ( str_len_do != str_len_posle); //проверка условия выхода из цикла

new_string = replace (new_string, "  ", " ";); //убираем лишние пробелы (контрольый :)

//Если вышли - выводим нашу строку
cout<<new_string;

 cin.get();
  delete new_string;
}


char* new_string="sadasd            sadasd f";    //наша строка

Надо ввести свою строку, проблемы:
1) Это ж указатель, а как записать то что ввиду с калвы через указатель я не пойму :(
2) Знаю что выделять память нужно, но опять таки... если введённая строка будет больше чем я памяти выделю? Я ж не знаю сколько заранее строка будет символов...

Ответить

Номер ответа: 55
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #55 Добавлено: 15.11.05 14:02
Хм, видимо ты невнимательно читал ф-цию replace :) Там специально возвращается ноль, если нет вхождений подстроки в строку. И не надо считать str_len_do, str_len_posle. И напрасных действий не будет.
На этом можно сэкономить все твои вызовы strlen и сделать цикл очень просто
Кроме того, ты выделяешь в цикле новые строки, не удаляя старые. Не хорошо.

И в чём проблема со вводом строки с клавиатуры? Засовывай в буфер заведомо достаточной длины:

    char    String[128];        //буфер для строки "sadasd        sadasd  f"
    char    Match[128];         //буфер для заменяемой строки (два пробела)
    char    Replace[128];       //буфер для заменяющей строки (один пробел)
    char*   new_string;
    char*   temp;

    printf ( "Input string: " );
    read_console( String, sizeof(String) );     //получаем строку
    printf ( "Input match string: " );          
    read_console( Match, sizeof(Match) );       //получаем два пробела  
    printf ( "Input replace string: " );
    read_console( Replace, sizeof(Replace) );   //получаем замену

    /* временная строка */
    temp = (char*) SysAllocStringByteLen( NULL, strlen(String)+1 );
    
    strcpy (temp, String);
    
    while ( (new_string = replace (temp, Match, Replace)) != 0 )
    {
        strcpy ( temp, new_string );
        /*удаляем new_string*/
        SysFreeString( (BSTR) new_string );
    }
    
    /* на выходе имеем строку "sadasd sadasd f"*/
    printf ("%s\n", temp);
    /*освобождаем временную строку*/
    SysFreeString( (BSTR) temp );


Если хочется иметь память под вводимую с клавиатуры строку точной длины, то можно конечно поставить обработчик исключений, и по исключению ACCESS_VIOLATION выделять новый кусок памяти и зачитывать уже в него. Но это геморрой, который ты сейчас вряд ли осилишь.
Возможно в с++ есть какие-то решения этой проблемы - не знаю. Пользоваться решениями из stdlib как-то не очень хочется.
Обрати внимание, что в ф-ции replace я таки заменил new char[required+1]; на
(char*) SysAllocStringByteLen(NULL,required);
Это надёжней и предсказуемей. И приведенный код замены пробелов не будет работать с new.
Можно вообще написать две простенькие ф-ции по выделению,обнулению первого байта и очистке памяти, и испольховать их вместо new и delete

Ответить

Номер ответа: 56
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #56 Добавлено: 15.11.05 20:44
кста, меня за read_console сегодня препод по программированию выругал, сказал что это мало того что велосипед самодельный ещё и извращение :) может лучше использовать вмесно неё cin.getline ... ??? Но всёравно, спасибо!

Ответить

Номер ответа: 57
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #57 Добавлено: 15.11.05 21:09
В баню препода, вместе с его cin.getline !!!
API forever!!!

Ответить

Номер ответа: 58
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #58 Добавлено: 16.11.05 13:40
ну говорит мол лучше использовать готовые "приспособления" от самого C++ чем изобретать самодельные типа read_console... :) Я конечно гривой покивал :) спорить не с тобой не с ним немогу :) от если б по VB начал бы меня доставать, ото я б в бой ринулся :) а с С++ пока немогу :)

Ещё вопрос... точнее у меня препод спросил а я ответить не сомг, расскаж плиз...

P.S. (возращаемся к студентам)
    array = new STUDENT[STUDENT_COUNT];    
    memset ( array, 0, STUDENT_COUNT*sizeof(STUDENT) );


Зачем там memset? Когда закоментировал тоже работает...
Ведь array = new STUDENT[STUDENT_COUNT]; и так создаёт массив и выделят память под него...

Ответить

Номер ответа: 59
Автор ответа:
 HACKER


 

Разработчик Offline Client

Вопросов: 236
Ответов: 8362
 Профиль | | #59 Добавлено: 16.11.05 14:03
:( блин и replace вылетает

/*
Задание: Удалить в тексте лишние пробелы

  Реализация функции Replace !
  
*/

#include <windows.h>
    #include <iostream.h>
    
//==========================================================
char* replace ( char* lpString, char* lpMatch, char* lpReplace )
{
    int     match_count=0;
    int     str_len = strlen(lpString);
    int     match_len = strlen(lpMatch);
    int     repl_len = strlen(lpReplace);
    int     required;
    char*   pNext;
    char*   pDest;      
    pNext = lpString;
    /* считаем кол-во вхождений подстроки lpMatch в lpString */
    while ( (pNext = strstr(pNext,lpMatch)) != NULL )
    {
        match_count++;  /* увеличиваем счётчик */
        pNext++;        /* смещаемся на найденую позицию */
    }  
// если нет вхождений - возвращаем ноль
    if ( ! match_count ) return 0;
    // определение длины выходной строки
    required = str_len - match_len*match_count + repl_len*match_count;
    // выделение места для вых. строки и её обнуление
char* output = new char[required+1];
    output[0]=0;
    pNext = lpString;
    // пока в строке будут встречаться lpMatch, будем заменять
    // на lpReplace, сдвигаясь после каждой замены по строке  
    do
    {
        pDest = strstr(pNext,lpMatch);
        // если все замены сделаны - выходим
        if ( ! pDest ) break;        
        // копируем в вых. строку фрагмент до lpMatch
        strncat(output,pNext,(pDest-pNext));
        // вместо lpMatch цепляем lpReplace
        strcat(output,lpReplace);
        pNext = pDest+match_len;
    }
    while (1);
    // дописываем в вых. строку конец строки
    strcat(output,pNext);      
    // возвращаем указатель на получившуюся строку    
return output;
}

void main(){
    char    String[128];        //буфер для строки "sadasd        sadasd  f"
    char    Match[128]= " ";         //буфер для заменяемой строки (два пробела)
    char    Replace[128]= "  ";       //буфер для заменяющей строки (один пробел)
    char*   new_string;
    char*   temp;

    cout<<"Input string: \n";
    cin.getline (String,128);

    /* временная строка */
    temp = (char*) SysAllocStringByteLen( NULL, strlen(String) +1 );      
    
strcpy (temp, String); //скопироали
     
    while ( (new_string = replace (temp, Match, Replace)) != 0 ) //цикл удаления пробелов
    {
        strcpy ( temp, new_string );
        /*удаляем new_string*/
        SysFreeString( (BSTR) new_string );
    }
     
    /* на выходе имеем строку "sadasd sadasd f"*/
    cout<<"\n"<<temp;
    /*освобождаем временную строку*/
    SysFreeString( (BSTR) temp );
cin.get();
}



User breakpoint called form code at 0x77f767cd

Ответить

Номер ответа: 60
Автор ответа:
 HOOLIGAN



Вопросов: 0
Ответов: 1066
 Профиль | | #60 Добавлено: 16.11.05 14:57
Хе-хе, а я вот всё жду, когда же ты на вылет проги по replace нарвешься:)
Долго ждал :) Короче, есть такая трабла: если многократно вызывать replace, то в какой-то момент прога вылетает с ошибкой, что инструкция по адресу 0x77f767cd обратилась к памяти 0х20202020 -память не может быть written. Хотя сама ф-ция нормально формирует строку. Как я понял, это кто-то из libc-овых strstr, strncat или strcpy что-то некорректно делает. Копаться в кишках ntdll, где происходит ошибка неохота, поэтому просто переделал replace, чтобы ни одной ф-ции из неё не вызывать. Получилось достаточно ничего. Многократный вызов никаких ошибок не вызывает.

//==========================================================
char* replace ( char* lpString, char* lpMatch, char* lpReplace )
{
    int match_count=0;
    int str_len=0;
    int match_len=0;
    int repl_len=0;
    int required;
    char* pOut;
    char* pSrc;
    char* output;
    int i=0;
    
    while ( lpString[str_len] ) str_len++; //длина lpString
    if ( !str_len ) return 0;
    while ( lpMatch[match_len] ) match_len++; //длина lpMatch
    if ( !match_len ) return 0;
    while ( lpReplace[repl_len] ) repl_len++; //длина lpReplace

    pSrc = lpString;
    while ( pSrc[0] ) //подсчёт кол-ва вхождений lpMatch в lpString
    {
        i=0;
        while ( pSrc && (pSrc==lpMatch) ) i++;
        if ( i == match_len )
        {
            match_count++;
            pSrc += i;
        }
        else pSrc++;
    }

    if ( ! match_count ) return 0;
    
    required = str_len - ((match_len- repl_len)*match_count); //длина новой строки
    if ( 0 == (output = new char[required+1]) ) return 0; //и получение памяти под неё
    pOut = output;
    pSrc = lpString;
    
    while ( pSrc[0] ) //поиск фрагментов и замена на новые
    {
        if ( pSrc[0] != lpMatch[0] ) //копируем символы не подлежащие замене, в вых.строку
        {
            pOut[0] = pSrc[0];
            pOut++; pSrc++;
        }
        else //если встретился символ, с которого начинается lpMatch
        {
            i = 0;
            while ( pSrc && (pSrc==lpMatch) ) i++;
            if ( i == match_len ) //и lpMatch найдена целиком
            {
                for (i=0; i<repl_len; i++) //вместо неё в вых. строку копируем lpReplace
                    pOut = lpReplace;
                pOut += i; pSrc += match_len;
            }
            else pSrc++; //если совпал только первый символ lpMatch, продолжаем
        } //копировать старую строку в вых. строку
    }
    pOut[0]=0; //завершаем вых. строку нулём
    
    return output;
    
}

выделение памяти сделано через new, освобождение значит должно быть через delete. Но можно и использовать SysAllocStringByteLen/SysFreeString. Разницы нет.
Пробуй :)


А что касается memset ( array, 0, STUDENT_COUNT*sizeof(STUDENT) ); то это после выделения памяти под массив структур, производится очистка этой памяти - затирание нулями. На всякий случай. После выделения там всякий мусор, вот и обнуляем эту память. Вообще, эти ф-ции описаны в msdn, можно прочитать, что они делают.

Ответить

Страница: 1 | 2 | 3 | 4 | 5 |

Поиск по форуму



© Copyright 2002-2011 VBNet.RU | Пишите нам