C Низове с различна дължина, малки към главни букви

Опитвам се да внедря функция, която получава c низ като вход, преобразува всички малки букви в главни, след което съхранява резултата в изходния параметър. Ето кода за тази функция:

void makeUpper( const unsigned char* input, unsigned char* output ) 
{

    int inputLength = strlen((char*)input);
    int outputLength = strlen((char*)output);

    for (int i = 0; i < inputLength; i++)
    {
        if ((input[i] >= 97) && (input[i] <= 122))
        {
            output[i] = input[i] - 32;
        }
        else
        {
            output[i] = input[i];
        }
    }

}

Сега очевидно ще възникне проблем в случай, че inputLength > outputLength. За да поправя това, вмъкнах следния код между декларациите inputLength & outputLength и for цикъла.

if (inputLength > outputLength)
{
    for (int i = 0; i < (inputLength - outputLength); i++)
    {
        strcat((char*)output, " ");
    }
}

Това не само води до грешка (тази функция или променлива може да не е безопасна...), но съм почти сигурен, че действам по грешен начин. Въпреки това не мога да се сетя за алтернативи.

РЕДАКТИРАНЕ:

Основната функция, която използвам, е следната:

int main() 
{

  unsigned char in[] = "HELLO aaaaaaaaaa 678";
  unsigned char out[] = "                    xxxxxxxxx";

  makeUpper( in, out );
  cout << in << " -> " << out << endl;
  makeUpper( out, in );
  cout << out << " -> " << in << endl;

 return 0;

}

Това, което функцията трябва да отпечата е:

HELLO aaaaaaaaaa 678 -> HELLO AAAAAAAAAA 678xxxxxxxxx
HELLO AAAAAAAAAA 678xxxxxxxxx -> HELLO AAAAAAAAAA 678XXXXXXXXX

person Will    schedule 29.10.2013    source източник
comment
Трябва ли буферът да се предава от потребители на функцията или можете сами да malloc буфера?   -  person Ian McLaird    schedule 30.10.2013
comment
@IanMcLaird: strncat() е толкова смъртоносен, колкото strcat(), главно защото трябва да знаете колко данни вече са съхранени в целевия низ, за ​​да можете да посочите правилно дължината, но ако знаете това, едва ли трябва да използвате функция *cat() в всичко.   -  person Jonathan Leffler    schedule 30.10.2013
comment
Или трябва да приемете, че има достатъчно място в целевия низ (по-просто), или трябва да предадете дължината на целевия низ на функцията като аргумент.   -  person Jonathan Leffler    schedule 30.10.2013
comment
@JonathanLeffler, ти си абсолютно прав и изтрих предишния си коментар (надявам се), преди някой да го приеме като добър съвет.   -  person Ian McLaird    schedule 30.10.2013


Отговори (2)


Вие обърквате „настоящото съдържание на изходния параметър“ с „налично пространство“. Първото е без значение, а единствената информация, която имате за второто, е „има поне толкова свободно място“.

Сега ще зависи от това как на първо място е зададено пространството за output. Ако сте направили нещо подобно

char output[100];
strcpy(output, "hello");

Ще получите място за 100 знака, но реално използвани само 6 (5+1). Следователно можете да вземете низа "THIS IS a STRING" и да го обработите с вашата функция без проблем.

Но това не е безопасно, защото не знаете колко място има. Следният подход би бил по-добър:

char *output;
output = malloc(100);

Сега променете своя прототип на функция на

void makeUpper( const unsigned char* input, unsigned char** output ) 

и във вашата функция, вие го правите

inputLength = strlen(input);
*output = realloc(*output, inputLength + 1);

Това ще гарантира, че е отделено достатъчно място за изхода. Или можете да върнете стойността във входния вектор - вече знаете, че там има достатъчно място...

РЕДАКТИРАНЕ В примера, който давате, има достатъчно място в output; въпросът просто се превръща в "безопасно копиране" на (с главни букви) вход към изхода. В който случай вашата функция може да изглежда така:

void makeUpper( const unsigned char* input, unsigned char* output ) 
{

    int inputLength = strlen(input);
    int outputLength = strlen(output);
    int ii;
    for (ii = 0; ii < inputLength; ii++)
    {
        output[ii] = toupper(input[ii]);
    }
    if(outputLength < inputLength) output[ii] = '\0';
}

Последният ред е там, за да се уверите, че ако сте увеличили дължината на output (отново, ако приемем, че това е памет, до която имате валиден достъп), тогава все пак трябва да се уверите, че има завършващ знак nul в края на низа. Във вашия пример искате „останалата част от изходния низ“ все още да е там, когато input е по-къс от output, така че имате нужда от условието if.

Като цяло - ако не знаете със сигурност, че output е достатъчно голям, няма начин да го направите по-голям, без да имате достъп до адреса на указателя - понякога наричан "дръжка".

person Floris    schedule 29.10.2013
comment
Има ли начин да направите това, без да променяте прототипа? - person Will; 30.10.2013
comment
Друг въпрос, когато стартирам програмата, всичко се отпечатва правилно, но се получава грешка: Стекът около променливата „in“ е повреден. Дали това е така, защото повече символи се съхраняват в [], отколкото първоначално са били разпределени? - person Will; 31.10.2013
comment
Когато направите второто извикване и копирате out обратно в in, вие наистина превишавате наличното пространство в in и може да доведете до презаписване на части от out (Въпреки че това, което се поврежда, зависи от това как компилаторът избира да разпредели стека - няма фиксирани правила). Така че да - не го правете! - person Floris; 31.10.2013

Ако може да се приеме, че изходният буфер е създаден във функцията:

// C++ version
void makeUpper(const unsigned char* input, unsigned char*& output)
{
    // assume output = null
    int inputLength = strlen((const char*)input);
    output = new unsigned char[inputLength + 1];
    memset(output, 0, inputLength + 1); // initialize the array to 0's

    for (int i = 0; i < inputLength; i++)
    {
        output[i] = ::toupper(input[i]); // why reinvent the wheel
    }
}

// C version
void makeUpper(const unsigned char* input, unsigned char** output) 
{
    // assume output = null
    int inputLength = strlen((const char*)input);
    *output = (unsigned char*)malloc((inputLength + 1) * sizeof(unsigned char));
    memset(*output, 0, (inputLength + 1) * sizeof(unsigned char)); // initialize the array to 0's

    for (int i = 0; i < inputLength; i++)
    {
        (*output)[i] = ::toupper(input[i]);
    }
}

output ще трябва да бъде изтрит/освободен от всеки, който поеме контрола над него.

Ако не искате правилно да оразмерите output във функцията:

void makeUpper(const unsigned char* input, unsigned char* output, unsigned int output_size) 
{
    // assume output != null, the current contents of output are irrelevant - you need it's size
    int inputLength = strlen((const char*)input);
    int maxLength = (inputLength < output_size - 1 ? inputLength : output_size - 1);
    memset(output, 0, output_size); // clear output

    for (int i = 0; i < maxLength; i++)
    {
        output[i] = ::toupper(input[i]); // why reinvent the wheel
    }
}

ЗАБЕЛЕЖКА: Тагът C++ беше премахнат, докато пишех отговора си. Това наистина засяга само първото решение (тъй като бихте използвали malloc вместо new). Бих се отклонил от realloc или calloc, освен ако вашите изисквания не ги налагат абсолютно.

person Zac Howland    schedule 29.10.2013
comment
Не можете да използвате препратки и в C решение - засягайки първото ви решение малко повече. - person Jonathan Leffler; 30.10.2013
comment
Оттук и коментарът с двоен указател под него. За по-голяма яснота го разделих на C++ и C версия. - person Zac Howland; 30.10.2013