0

Есть проблема с выводом символом на экран.

Когда посимвольно считываю строку из файла, то выскакивает символ "?" в прямоугольнике.

Замена кодировки к Windows-1251 или UTF-8 ни к чему не привела.

// Для решения задачи вам нужно ввести строки с помощью функций gets или fgets, разбить строку 
// на слова и выбрать нужные (функция scanf с форматом %s вводит строку до пробела, но в 
// данной задаче вам не нужно так делать!). Стандартные функции работы со строками НЕ 
// использовать! Обратите внимание, что во всех случаях слова могут разделяться любым(!) 
// количеством символов, не относящихся к слову(будем считать, что к слову относятся большие и 
// маленькие латинские буквы и цифры). Желательно всю обработку выполнить за один проход 
// строки, хотя это возможно не во всех вариантах. Обязательно проверить работу программы на 
// пустой строке и на строке, состоящей только из символов, не относящихся к слову. 
// Сформировать строку, добавляя к каждой заданной подстроке другую заданную подстроку.

#include <stdio.h> #include <stdlib.h> #include <locale.h> #include <windows.h>

#define _CRT_SECURE_NO_WARNINGS

int DefineSize(FILE* file); void ReadStr(FILE* file, char* str); char* FormNewStr(char* str_one, int n_one, char* str_two, int n_two, char* str_three, int n_three); int FindSubstr(char* str, int n, char* substr, int ns); char* Concat(char* str, int n, char* substr, int ns, int index); void Show(char* str);

int main(int argc, char* argv[]) { FILE* file_one, * file_two, * file_three; char* str_one, * str_two, str_three, new_str; int n_one, n_two, n_three;

SetConsoleCP(1251);
SetConsoleOutputCP(1251);

setlocale(LC_ALL, &quot;rus&quot;);

if (argc &lt; 3)
{
    printf(&quot;Недостаточное число параметров.\n&quot;);
    return 0;
}

fopen_s(&amp;file_one, argv[1], &quot;r&quot;);
fopen_s(&amp;file_two, argv[2], &quot;r&quot;);
fopen_s(&amp;file_three, argv[3], &quot;r&quot;);
if (file_one == nullptr || file_two == nullptr || file_three == nullptr)
{
    printf(&quot;Не все файлы находятся в директории проекта.\n&quot;);
    return 0;
}
else
{
    if (feof(file_one) || feof(file_two) || feof(file_three)) 
    { 
        fclose(file_one); 
        fclose(file_two); 
        fclose(file_three); 

        printf(&quot;Какие-то текстовые файлы пустые.\n&quot;); 

        return 0; 
    }
    else
    {
        fseek(file_one, 0, SEEK_CUR);
        fseek(file_two, 0, SEEK_CUR);
        fseek(file_three, 0, SEEK_CUR);

        n_one = DefineSize(file_one);
        str_one = (char*)malloc(sizeof(char) * n_one);

        n_two = DefineSize(file_two);
        str_two = (char*)malloc(sizeof(char) * n_two);

        n_three = DefineSize(file_three);
        str_three = (char*)malloc(sizeof(char) * n_three);

        fseek(file_one, 0, SEEK_CUR);
        fseek(file_two, 0, SEEK_CUR);
        fseek(file_three, 0, SEEK_CUR);

        ReadStr(file_one, str_one);
        ReadStr(file_two, str_two);
        ReadStr(file_three, str_three);

        fclose(file_one);
        fclose(file_two);
        fclose(file_three);

        Show(str_one);
        Show(str_two);
        Show(str_three);

        new_str = FormNewStr(str_one, n_one, str_two, n_two, str_three, n_three);

        Show(new_str);

        free(str_one);
        free(str_two);
        free(str_three);
        free(new_str);

        return 0;
    }
}

}

int DefineSize(FILE* file) { int n = 0; char ch;

while (ch = fgetc(file) != EOF)
{
    n++;
    printf(&quot;%c &quot;, ch);
}
printf(&quot;\n&quot;);

return n;

}

void ReadStr(FILE* file, char* str) { int counter = 0; char ch;

while (ch = fgetc(file) != EOF)
{
    str[counter] = ch;
    counter++;
}

}

char* FormNewStr(char* str_one, int n_one, char* str_two, int n_two, char* str_three, int n_three) { if (str_one != nullptr && str_two != nullptr && str_three != nullptr) { int index = FindSubstr(str_one, n_one, str_two, n_two); char* new_str = str_one;

    while (index != -1)
    {
        char* auxilary_str = (char*)malloc(sizeof(char) * (n_one - index - n_two));

        if (auxilary_str != nullptr)
        {
            for (int i = index + n_two; i &lt; n_one; i++)
            {
                auxilary_str[i - index - n_two] = str_one[i];
            }

            new_str = Concat(str_one, n_one, str_three, n_three, index);
            index = FindSubstr(auxilary_str, n_one - index - n_two, str_two, n_two);
        }
    }

    return new_str;
}
else
{
    return nullptr;
}

}

int FindSubstr(char* str, int n, char* substr, int ns) { if (ns > n) { return -1; } else { bool flag = true;

    for (int i = 0; i &lt; n - ns + 1; i++)
    {
        for (int j = 0; j &lt; ns; j++)
        {
            if (str[j + i] != substr[j])
            {
                flag = false;
            }
        }

        if (flag)
        {
            return i;
        }
    }

    return -1;
}

}

char* Concat(char* str, int n, char* substr, int ns, int index) { char* new_str = (char)malloc(sizeof(char) (n + ns));

if (new_str != nullptr)
{
    for (int i = 0; i &lt; index; i++)
    {
        new_str[i] = str[i];
    }

    for (int i = index; i &lt; index + ns; i++)
    {
        new_str[i] = substr[i - index];
    }

    for (int i = index + ns; i &lt; n; i++)
    {
        new_str[i] = str[i];
    }

    return new_str;
}
else
{
    return nullptr;
}

}

void Show(char* str) { if (str == nullptr) { printf("Строка пуста.\n"); } else { int counter = 0;

    while (str[counter] != '\0')
    {
        printf(&quot;%c &quot;, str[counter]);
        counter++;
    }
    printf(&quot;\n&quot;);
}

}

//file_one.txt qwcd

//file_two.txt cd

//file_three.txt 345

Результат отладки на 81-ой строчке(см. фото).

Объясните мне, тупице, где я что не так делаю?

Кодировки менял и в блокноте, и здесь. Бесполезно.

Что не так-то?

Меня даже не так волнует, правильно ли я сделал задание, но вот стопор на этом..введите сюда описание изображения

Harry
  • 221,325
  • Это не си код. В си нет nullptr. И при вызовах (m/c/re)alloc явное приведение указателя не требуется. – evo Dec 24 '21 at 21:12
  • Спасибо. Я это учту. Конечно, я на С делал. Просто написал nullptr случайно. – Александр Скворцов Dec 24 '21 at 21:13
  • А что Вы имели в виду фразой "И при вызовах (m/c/re)alloc явное приведение указателя не требуется"? Я где-то ошибся? Не могу понять этого... – Александр Скворцов Dec 24 '21 at 21:20
  • @Kotomi в смысле "явное приведение указателя не требуется"?? Функции (m/c/re)alloc возвращают тип void*, явное приведение которого как раз таки очень требуется. В противном случае получите ошибку invalid conversion from void* to... (или похожее сообщение) - можете проверить. А вот при вызовах free() тип переданного указателя приводить обратно к void* не обязательно, компилятор сделает это неявно. – LShadow77 Dec 24 '21 at 21:33
  • В чём моя ошибка с выводом символов? Я уже и в настройках консоли менял шрифт. Кодировку везде, где только можно... Всё без толку. – Александр Скворцов Dec 24 '21 at 21:42
  • Я исправил ошибку, но теперь другое. Почему malloc выдляет больше памяти, чем надо? С чем это связано? – Александр Скворцов Dec 24 '21 at 23:01
  • "Почему malloc выдляет больше памяти, чем надо?" -- какой из нескольких malloc-ов и как вы это обнаружили (откуда узнали, сколько именно памяти выделено)? – avp Dec 24 '21 at 23:19
  • При отладке навел курсор на указатели (char*), а там большее число символов, которые отображаются функцией Show() – Александр Скворцов Dec 24 '21 at 23:22
  • Я имел в виду, что функция ищет конец строки, но она находит его не там, а вне массива, что, конечно, разрешено делать в С(в Java JVM ругалась бы и сгенерировала бы исключение). – Александр Скворцов Dec 24 '21 at 23:29
  • Непонятно, почему malloc выдялет больше, чем надо. Там дальше идёт мусор всякий. Я пробовал ради интереса скопировать участок кода realloc'ом, но все то же самое, к сожалению. – Александр Скворцов Dec 24 '21 at 23:35
  • "В противном случае получите ошибку invalid conversion from void to... (или похожее сообщение) - можете проверить.* — вы можете проверить, скомпилировав код как С, а не как С++, что в С это приведение возвращаемого типа действительно не требуется. https://ideone.com/vK3WrC – Harry Dec 25 '21 at 05:34
  • Еще — при открытии, каким бы пустым файл ни был — проверка if (feof(file_one)... ничего не даст. Признак конца файла выставляется только после неудачного чтения. – Harry Dec 25 '21 at 05:53
  • Скопировал ваш код, перенес в редактор. 81 строчка — между fseek(file_three, 0, SEEK_CUR); и ReadStr(file_one, str_one);... – Harry Dec 25 '21 at 05:57
  • Еще — после определения размера файла вы его не отматываете назад. Должен огорчить, но fseek(file_one, 0, SEEK_CUR); — это оставить указатель файла на месте. Выставить на начало — это fseek(file_one, 0, SEEK_SET);. – Harry Dec 25 '21 at 06:01
  • функция fgetc возвращает тип int, а вы сразу присваиваете укороченному типу char. Что приведёт к неправильному решению конца файла, если там будет буква с кодом \255. Компил'ируете в Си, а не плюсами. – AlexGlebe Dec 25 '21 at 10:56

1 Answers1

1

Я бы настойчиво рекомендовал исправить самую главную ошибку:

Выражения

while (ch = fgetc(file) != EOF)

заменить на

while ((ch = fgetc(file)) != EOF)

(вспомните о приоритете операций сравнения и присваивания).

Ну, и остальные...

Например, в DefineSize возвращать не n, а n+1

В ReadStr последней строкой добавить str[counter] = 0;.

Все SEEK_CUR заменить на SEEK_SET.

Дальше — как вы там форматируете новую строку — не смотрел, да вы об этом и не спрашивали.

Harry
  • 221,325
  • Привет! Всё исправил. Сначала шло, а потом опять стали кракозябры выскакивать. Точнее работало всё очень хорошо до тех пор, пока я не начал тестировать альт-коды. Кодировка сбилась полностью. Команды setlocale(LC_ALL), SetConsoleCP(1251) и SetConsoleOutputCP(1251) не помогли. В Microsoft Visual Studio. А вот в Dev-cpp всё работает. В чём может быть причина? – Александр Скворцов Dec 27 '21 at 18:40
  • Я писал, что смотрел только до new_str = FormNewStr(str_one......, дальше смотрите сами. Если выскакивает до этого — значит, взяли и, например, записали в файлы русский текст и не учли кодировку... Альт-коды — что вы имеете в виду? – Harry Dec 27 '21 at 18:42
  • https://ru.wikipedia.org/wiki/Alt-%D0%BA%D0%BE%D0%B4 – Александр Скворцов Dec 27 '21 at 18:44
  • Это всего лишь иной способ ввода. И этот вопрос связан с кодировкой и выходит за рамки исходного вопроса. Так что здесь уже нужно задавать новый вопрос. Только скорее всего он будет закрыт как дубль этого вопроса: https://ru.stackoverflow.com/q/459154/195342 Рекомендую посмотреть ответы по указанной ссылке. – Harry Dec 27 '21 at 18:48
  • Здравствуйте снова! Решилась проблема тем, что Notepad++ по умолчанию создавал txt-файлы с кодировкой UTF-8 с BOM. Я поставил, чтобы изначально была Windows-1251. – Александр Скворцов Dec 27 '21 at 19:41
  • Ну, примерно это я и подразумевал под значит, взяли и, например, записали в файлы русский текст и не учли кодировку... Если все прояснилось и ответ вас устраивает, то птичка слева от него к вашим услугам :) – Harry Dec 27 '21 at 19:44
  • "...то птичка слева от него к вашим услугам :)" Вы имеете в виду, что поставить вам плюсик? Как тут это сделать? – Александр Скворцов Dec 27 '21 at 19:52
  • Здесь принято в конечном итоге закрывать вопрос, принимая тот или иной ответ. Птичка слева от ответа служит для его принятия. – Harry Dec 27 '21 at 19:53
  • А почему мой malloc выделяет больше, чем надо? Я выделил, допустим, 10 байт, заполнил 10 символами, потом вывожу на экран строку, а там после 10 символов выводятся ещё символы. Что делать? – Александр Скворцов Dec 28 '21 at 13:09
  • Я же написал — Например, в DefineSize возвращать не n, а n+1. В ReadStr последней строкой добавить str[counter] = 0;. Именно для того, чтобы выводилось ровно n символов. Строка в С заканчивается нулевым символом. – Harry Dec 28 '21 at 14:26
  • Теперь вылезает ошибка "HEAP CORRUPTION DETECTED". Но в Dev-CPP этой ошибки нет. Ерунда какая-то. – Александр Скворцов Dec 28 '21 at 23:41
  • Если вы сделали все, как я говорил — то это уже в не рассмотренных мною функциях формата и т.д. Вот, например, в Concat вы так и оставили char* new_str = (char*)malloc(sizeof(char) * (n + ns));, или тоже исправили, добавив дополнительный байт для нулевого символа? Или вы хотите, чтоб я просто написал за вас полностью задание? но тогда и сдавать его преподу должен я... – Harry Dec 29 '21 at 05:27
  • Нет, я не хочу, чтобы Вы писали, так как мне самому это интересно. – Александр Скворцов Dec 29 '21 at 10:40