fstream перестает читать при замене управляющего символа - PullRequest
0 голосов
/ 16 мая 2018

Я пишу простую программу шифрования на C ++ для шифрования текстового файла. Он использует простой алгоритм шифрования XOR, но он производит управляющие символы ASCII в выходном файле. Когда я пытаюсь прочитать из только что зашифрованного файла с std::ifstream, он наталкивается на символ #26, останавливается и становится неспособным прочитать остальную часть файла.

Пример, если я пытаюсь зашифровать этот текст:

This is just a simple sample
text with two rows and one sentence.

Это превращается в это

/[[[[[
[[[ [[[U

Когда я пытаюсь прочитать этот файл в моей программе, он не может прочитать после символа в позиции 15, поэтому я получаю полушифрованный файл.

Как я могу это исправить?

Вот код:

#include <iostream>
#include <Windows.h>
#include <string>
#include <fstream>

void Encrypt(char encryptionKey, std::string filename)
{
    std::ifstream sourceFile(filename);
    std::ofstream outputFile(filename.substr(0, filename.find_last_of("\\")) + "\\Encrypted" + filename.substr(filename.find_last_of("\\") + 1), std::ofstream::out | std::ofstream::trunc);
    std::string sourceLine;
    std::string outputLine;
    long numLines = 0;
    if (sourceFile.is_open())
    {
        std::cout << "Opening file: " + filename + " for encryption" << std::endl;
        while (sourceFile.good()) // This iterates over the whole file, once for each line
        {
            sourceLine = ""; //Clearing the line for each new line
            outputLine = ""; //Clearing the line for each new line

            std::getline(sourceFile, sourceLine);
            for (int i = 0; i < sourceLine.length(); i++) // Looping through all characters in each line
            {

                char focusByte = sourceLine[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                outputLine.push_back(focusByte);
                //std::cout << sourceLine << std::flush;

            }
            numLines++;
            outputFile << outputLine << std::endl;
        }
    }
    sourceFile.close();
    outputFile.close();
}

void Decrypt(unsigned int encryptionKey, std::string filename)
{
    std::ifstream sourceFile(filename);
    std::ofstream outputFile(filename.substr(0, filename.find_last_of("\\")) + "\\Decrypted" + filename.substr(filename.find_last_of("\\") + 1), std::ofstream::out | std::ofstream::trunc);
    std::string sourceLine;
    std::string outputLine;
    long numLines = 0;
    if (sourceFile.is_open())
    {
        std::cout << "Opening file: " + filename + " for decryption" << std::endl;
        while (sourceFile.good()) // This iterates over the whole file, once for each line
        {
            if (sourceFile.fail() == true)
                std::cout << "eof" << std::endl;
            sourceLine = ""; //Clearing the line for each new line
            outputLine = ""; //Clearing the line for each new line

            std::getline(sourceFile, sourceLine);
            for (int i = 0; i < sourceLine.length(); i++) // Looping through all characters in each line
            {
                char focusByte = sourceLine[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                outputLine.push_back(focusByte);

            }
            numLines++;
            outputFile << outputLine << std::endl;
        }
    }
    sourceFile.close();
    outputFile.close();
}


int main(int argument_count,
    char * argument_list[])
{
    system("color a");
    std::string filename;
    if (argument_count < 2)
    {
        std::cout << "You didn't supply a filename" << std::endl;
    }
    else
    {
        filename = argument_list[1];
        std::cout << "Target file: " << filename << std::endl;
        std::cout << "Press e to encrypt the selected file, Press d to decrypt the file > " << std::flush;
        char choice;
        while (true)
        {
            std::cin >> choice;
            if (choice == 'e')
            {
                Encrypt(123, filename);
                break;
            }
            else if (choice == 'd')
            {
                Decrypt(123, filename);
                break;
            }
            else
            {
                std::cout << "please choose option e or d for encryption respectivly decryption" << std::endl;
            }
        }
    }


    std::cout << "\nPaused, press Enter to continue > " << std::flush;
    system("Pause");
    return EXIT_SUCCESS;
}

Ответы [ 3 ]

0 голосов
/ 16 мая 2018

Я скомпилировал ваш код в Linux (за исключением всех вещей Windows) ...

Я получаю это при шифровании вашего предложения с помощью кода:

/[[[[[
[[[ [[[U

Он также расшифровывает обратно воригинальное предложение.Без глупых символов это то же самое, что и ваш вывод, поэтому ваша реальная проблема, похоже, связана с кодировкой файла и программы, которую вы используете для просмотра результатов.Стефан прав, говоря, что вы должны читать / писать байты вместо текста.Это может вызвать всевозможные проблемы с персонажами, которых вы создаете.Например, переводы строки и возврат каретки, поскольку вы используете getline().

Редактировать: Странно.После редактирования этого ответа все нечетные символы исчезли.Вот скриншот: original output

0 голосов
/ 16 мая 2018

В Decrypt(), после первого вызова std::getline(), sourceFile.good() равно false, а sourceFile.fail() - true, поэтому вы прекращаете чтение последующих строк из зашифрованного файла.

Причинапотому что зашифрованный файл содержит закодированный байт 0x1A, и в зависимости от вашей платформы и реализации STL этот символ, скорее всего, интерпретируется как условие EOF, что позволяет состояние std::ifstream eofbit, что прекращает дальнейшее чтение.

В реализации моего компилятора STL для Windows, когда std::ifstream читает из файла, он в конечном счете вызывает функцию с именем _Fgetc():

template<> inline bool _Fgetc(char& _Byte, _Filet *_File)
    {   // get a char element from a C stream
    int _Meta;
    if ((_Meta = fgetc(_File)) == EOF) // <-- here
        return (false);
    else
        {   // got one, convert to char
        _Byte = (char)_Meta;
        return (true);
        }
    }

Когда он пытается прочитать 0x1A символ, fgetc() возвращает EOF, а когда _Fgetc() возвращает ложь, std::getline() устанавливает eofbit на std::ifstream и завершает работу.

Проверьте STL вашего компилятора на похожее поведение.

Это происходит потому, что вы открываете зашифрованный файл в текстовом режиме .Вместо этого вам нужно открыть зашифрованный файл в режиме binary:

std::ifstream sourceFile(..., std::ifstream::binary);

Также необходимо включить режим binary для зашифрованного файла в Encrypt():

std::ofstream outputFile(..., std::ofstream::binary | std::ofstream::trunc);

Вместо этого попробуйте что-то вроде этого:

#include <Windows.h>

#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>

void Encrypt(char encryptionKey, const std::string &filename)
{
    std::string::size_type pos = filename.find_last_of("\\");
    std::string out_filename = filename.substr(0, pos+1) + "Encrypted" + filename.substr(pos + 1);

    std::ifstream sourceFile(filename.c_str());
    std::ofstream outputFile(out_filename.c_str(), std::ofstream::binary | std::ofstream::trunc);

    if (sourceFile.is_open())
    {
        std::cout << "Opened file: " + filename + " for encryption" << std::endl;

        std::string line;
        long numLines = 0;

        while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
        {
            for (std::string::size_type i = 0; i < line.length(); ++i) // Looping through all characters in each line
            {
                char focusByte = line[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                line[i] = focusByte;
                //std::cout << line << std::flush;
            }

            outputFile << line << std::endl;
            ++numLines;
        }
    }
}

void Decrypt(char encryptionKey, const std::string &filename)
{
    std::string::size_type pos = filename.find_last_of("\\");
    std::string out_filename = filename.substr(0, pos+1) + "Decrypted" + filename.substr(pos + 1);

    std::ifstream sourceFile(filename.c_str(), std::ifstream::binary);
    std::ofstream outputFile(out_filename.c_str(), std::ofstream::trunc);

    if (sourceFile.is_open())
    {
        std::cout << "Opened file: " + filename + " for decryption" << std::endl;

        std::string line;
        long numLines = 0;

        while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
        {
            for (std::string::size_type i = 0; i < line.length(); ++i) // Looping through all characters in each line
            {
                char focusByte = line[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                line[i] = focusByte;
            }

            outputFile << line << std::endl;
            ++numLines;
        }

        std::cout << "eof" << std::endl;
    }
}

int main(int argument_count, char* argument_list[])
{
    std::system("color a");
    std::string filename;

    if (argument_count < 2)
    {
        std::cout << "Enter a file to process: " << std::flush;
        std::getline(std::cin, filename);
    }
    else
    {
        filename = argument_list[1];
    }

    if (filename.empty())
    {
        std::cout << "You didn't supply a filename" << std::endl;
        return EXIT_FAILURE;
    }

    std::cout << "Target file: " << filename << std::endl;
    std::cout << "Press e to encrypt the file" << std::endl;
    std::cout << "Press d to decrypt the file" << std::endl;
    char choice;

    while (true)
    {
        std::cout << "> " << std::flush;
        std::cin >> choice;

        if (choice == 'e')
        {
            Encrypt(123, filename);
            break;
        }
        else if (choice == 'd')
        {
            Decrypt(123, filename);
            break;
        }
        else
        {
            std::cout << "please choose option e or d for encryption or decryption, respectively" << std::endl;
        }
    }

    std::cout << std::endl << "Paused, press Enter to continue" << std::flush;
    std::system("pause");

    return EXIT_SUCCESS;
}

При этом имейте в виду, что при использовании XOR некоторые из зашифрованных символов могут в конечном итоге быть \r (0x0D) или \n (0x0A), который будет мешать std::getline() при последующем дешифровании файла, создавая дешифрованный вывод, который не соответствует исходному текстовому вводу.

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

Например:

#include <Windows.h>

#include <iostream>
#include <string>
#include <fstream>
#include <cstdlib>

void Encrypt(char encryptionKey, const std::string &filename)
{
    std::string::size_type pos = filename.find_last_of("\\");
    std::string out_filename = filename.substr(0, pos+1) + "Encrypted" + filename.substr(pos + 1);

    std::ifstream sourceFile(filename.c_str());
    std::ofstream outputFile(out_filename.c_str(), std::ofstream::binary | std::ofstream::trunc);

    if (sourceFile.is_open())
    {
        std::cout << "Opened file: " + filename + " for encryption" << std::endl;

        std::string line;
        std::string::size_type lineLen;
        long numLines = 0;

        while (std::getline(sourceFile, line)) // This iterates over the whole file, once for each line
        {
            lineLen = line.length();

            for (std::string::size_type i = 0; i < lineLen; ++i) // Looping through all characters in each line
            {
                char focusByte = line[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                line[i] = focusByte;
                //std::cout << line << std::flush;
            }

            outputFile.write((char*)&lineLen, sizeof(lineLen));
            outputFile.write(line.c_str(), lineLen);

            ++numLines;
        }
    }
}

void Decrypt(char encryptionKey, const std::string &filename)
{
    std::string::size_type pos = filename.find_last_of("\\");
    std::string out_filename = filename.substr(0, pos+1) + "Decrypted" + filename.substr(pos + 1);

    std::ifstream sourceFile(filename.c_str(), std::ifstream::binary);
    std::ofstream outputFile(out_filename.c_str(), std::ofstream::trunc);

    if (sourceFile.is_open())
    {
        std::cout << "Opened file: " + filename + " for decryption" << std::endl;

        std::string line;
        std::string::size_type lineLen;
        long numLines = 0;

        while (sourceFile.read((char*)&lineLen, sizeof(lineLen))) // This iterates over the whole file, once for each line
        {
            line.resize(lineLen);
            if (!sourceFile.read(&line[0], lineLen))
                break;

            for (std::string::size_type i = 0; i < lineLen; ++i) // Looping through all characters in each line
            {
                char focusByte = line[i] ^ encryptionKey;
                std::cout << " focusByte: " << focusByte << std::endl;
                line[i] = focusByte;
            }

            outputFile << line << std::endl;
            ++numLines;
        }

        std::cout << "eof" << std::endl;
    }
}

int main(int argument_count, char* argument_list[])
{
    std::system("color a");
    std::string filename;

    if (argument_count < 2)
    {
        std::cout << "Enter a file to process: " << std::flush;
        std::getline(std::cin, filename);
    }
    else
    {
        filename = argument_list[1];
    }

    if (filename.empty())
    {
        std::cout << "You didn't supply a filename" << std::endl;
        return EXIT_FAILURE;
    }

    std::cout << "Target file: " << filename << std::endl;
    std::cout << "Press e to encrypt the file" << std::endl;
    std::cout << "Press d to decrypt the file" << std::endl;
    char choice;

    while (true)
    {
        std::cout << "> " << std::flush;
        std::cin >> choice;

        if (choice == 'e')
        {
            Encrypt(123, filename);
            break;
        }
        else if (choice == 'd')
        {
            Decrypt(123, filename);
            break;
        }
        else
        {
            std::cout << "please choose option e or d for encryption or decryption, respectively" << std::endl;
        }
    }

    std::cout << std::endl << "Paused, press Enter to continue" << std::flush;
    std::system("pause");

    return EXIT_SUCCESS;
}
0 голосов
/ 16 мая 2018

Значение ASCII 26 в некоторых операционных системах равно EOF.

Вы, вероятно, должны рассматривать ваш зашифрованный файл как поток байтов, а не текстовый файл для чтения и записи. Это означает либо использование read() и write() функций IOStream, либо, по крайней мере, открытие файлов в двоичном режиме.

Если вы просто шифруете свой текст вместо шифрования, возможно, выберите другой шифр (например, ROT13), закрытый для набора печатаемых символов ASCII или UTF-8.

...