C удалить специальные символы из строки - PullRequest
1 голос
/ 27 июня 2019

Я очень новичок в C, и я создал функцию, которая удаляет специальные символы из строки и возвращает новую строку (без специальных символов).

На первый взгляд это работаетну, теперь мне нужно запустить эту функцию в строках (огромного) текстового файла (1 миллион предложений).После нескольких тысяч строк / предложений (около 4000) я получаю ошибку сегмента.

У меня нет большого опыта с выделением памяти и строками в C, я попытался выяснить, в чем проблема с моим кодомэто, к сожалению, без какой-либо удачи.Вот код:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

char *preproccessString(char *str) {
    // Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
    char *result = malloc(sizeof(str));
    // Array of allowed chars with a 0 on the end to know when the end of the array is reached, I don't know if there is a more elegant way to do this
    // Changed from array to string for sake of simplicity
    char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    // Initalize two integers
    // i will be increased for every char in the string
    int i = 0;
    // j will be increased every time a new char is added to the result
    int j = 0;
    // Loop over the input string
    while (str[i] != '\0') {
        // l will be increased for every char in the allowed chars array
        int l = 0;
        // Loop over the chars in the allowed chars array
        while (allowedCharsArray[l] != '\0') {
            // If the char (From the input string) currently under consideration (index i) is present in the allowed chars array
            if (allowedCharsArray[l] == toupper(str[i])) {
                // Set char at index j of result string to uppercase version of char currently under consideration
                result[j] = toupper(str[i]);
                j++;
            }
            l++;
        }
        i++;
    }
    return result;
}

Вот остальная часть программы, я думаю, что проблема, вероятно, здесь.

int main(int argc, char *argv[]) {
    char const * const fileName = argv[1];
    FILE *file = fopen(fileName, "r");
    char line[256];

    while (fgets(line, sizeof(line), file)) {
        printf("%s\n", preproccessString(line)); 
    }

    fclose(file);

    return 0;
}

Ответы [ 5 ]

5 голосов
/ 27 июня 2019

У вас есть несколько проблем.

  1. Вы не выделяете достаточно места. sizeof(str) - это размер указателя, а не длина строки. Вам нужно использовать
char *result = malloc(strlen(str) + 1);

+ 1 для завершающего нулевого байта.

  1. Вы не добавили завершающий нулевой байт в строку результата. Добавить
result[j] = '\0';

до return result;

  1. Как только вы обнаружите, что символ совпадает с разрешенным символом, нет необходимости повторять цикл по остальным разрешенным символам. Добавьте break после j++.

  2. Ваша функция main() никогда не освобождает результаты preprocessString(), поэтому, возможно, вам не хватает памяти.

while (fgets(line, sizeof(line), file)) {
    char *processed = preproccessString(line);
    printf("%s\n", processed); 
    free(processed);
}

Вы могли бы решить большинство из этих проблем, если бы вы передали вызывающую строку в результирующую строку вместо того, чтобы выделить ее в функции. Просто используйте два char[256] массива в функции main().

int main(int argc, char *argv[])
{
    char const* const fileName = argv[1];
    FILE* file = fopen(fileName, "r");
    char line[256], processed[256];

    while (fgets(line, sizeof(line), file)) {
        processString(line, processed);
        printf("%s\n", processed); 
    }

    fclose(file);

    return 0;
}

Затем просто измените функцию так, чтобы параметры были:

void preprocessString(const char *str, char *result)
1 голос
/ 27 июня 2019

В вашем коде есть несколько серьезных проблем:

  • неправильное количество выделенной памяти, sizeof(str) - количество байтов в указателе ,не длина строки, на которую она указывает, что также было бы неверно.Вы должны записать char *result = malloc(strlen(str) + 1);

  • память, выделенная в preproccessString, никогда не освобождается, что приводит к утечкам памяти и, возможно, к исчерпанию памяти программой на очень большие файлы.

  • вы не устанавливаете нулевой терминатор в конце строки result

Меньшие проблемы:

  • вы не проверяете, было ли передано имя файла и не удалось ли fopen().
  • в preproccessString есть опечатка, она должна быть preprocessString
  • , которую вы могли бы избежатьВыделение памяти путем передачи целевого массива правильного размера.
  • Вы можете использовать isalpha вместо проверки каждой буквы
  • . Вы должны преобразовать значения char в unsigned char при передаче их toupper, поскольку char может быть типом со знаком, а toupper не определено для отрицательных значений, за исключением EOF.
  • в исходном файле слишком много комментариев, большинство из которых очевидны, но составляют кодменее читабелен.

Вот модифицированная версия:

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

// transform the string in `str` into buffer dest, keeping only letters and uppercasing them.
char *preprocessString(char *dest, const char *str) {
    int i, j;
    for (i = j = 0; str[i] != '\0'; i++) {
        if (isalpha((unsigned char)str[i])
            dest[j++] = toupper((unsigned char)str[i]);
    }
    dest[j] = '\0';
    return dest;
}

int main(int argc, char *argv[]) {
    char line[256];
    char dest[256];
    char *filename;
    FILE *file;

    if (argc < 2) {
        fprintf(stderr, "missing filename argument\n");
        return 1;
    }
    filename = argv[1];
    if ((file = fopen(filename, "r")) == NULL) {
        fprintf(stderr, "cannot open %s: %s\n", filename, strerror(errno));
        return 1;
    }
    while (fgets(line, sizeof(line), file)) {
        printf("%s\n", preprocessString(dest, line)); 
    }
    fclose(file);

    return 0;
}
1 голос
/ 27 июня 2019

следующий предложенный код:

  1. чисто компилирует
  2. выполняет желаемую функциональность
  3. правильно проверяет наличие ошибок
  4. правильно проверяет длину входного строкового параметра
  5. использует характеристику strchr(), также проверяя завершающий байт NUL
  6. ограничивает область видимости локальных переменных
  7. Ожидается, что вызывающая функция будет корректно очищена путем передачи возвращенного значения в free()
  8. Ожидается, что вызывающая функция проверит возвращаемое значение для NULL
  9. сообщает компилятору, что пользователь знает и принимает, когда выполняется неявное преобразование.
  10. перемещает allowedCharsArray в «статическую область файла», поэтому не требуется повторная инициализация при каждом проходе цикла и помечается как «const», чтобы помочь компилятору перехватывать ошибки

и теперь предлагаемый код: (примечание: отредактировано в комментариях)

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

char *preproccessString(char *str) 
{
    // Create a new string of the size of the input string, so this might be bigger than needed but should never be too small
    char *result = calloc( sizeof( char ),  strlen(str)+1);
    if( !result )
    {
        perror( "calloc failed" );
        return NULL;
    }

    // Array of allowed chars 
    static const char *allowedCharsArray = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    // Loop over the input string
    for( int  j=0, i=0; str[i]; i++) 
    {
        if( strchr( allowedCharsArray, (char)toupper( str[i] ) ) )
        {
            // Set char at index j of result string to uppercase version of char currently under consideration
            result[j] = (char)toupper(str[i]);
            j++;
        }
    }
    return result;
}
1 голос
/ 27 июня 2019

Хорошее практическое правило - убедиться, что для каждого вызова malloc / calloc есть один бесплатный.

Кроме того, хорошим инструментом, который стоит помнить на будущее, является Valgrind. Очень хорошо ловить подобные ошибки.

0 голосов
/ 27 июня 2019

Я думаю, что проблема в том, что вы используете malloc, который выделяет память из кучи, и, поскольку вы снова и снова вызываете эту функцию, у вас заканчивается память. Чтобы решить эту проблему, вы должны вызвать функцию free () для указателя, возвращенного вашей функцией preprocessString В вашем основном блоке

char *result=preprocessString(inputstring);
//Do whatever you want to do with this result
free(result);
...