Ir ao conteúdo
  • Cadastre-se

Arquivos: questão cifra de césar


HelpMePlease

Posts recomendados

A cifra de César é uma forma de criptografia antiga, onde cada letra de uma determinada mensagem é trocada pela letra que a sucede em 3 posições. Por exemplo, a palavra AJUDA seria transformada em DMXGD. Escreva uma função que leia um arquivo texto e escreva a cifra de César correspondente em um outro arquivo texto.

A letra X é trocada por A, Y por B e Z por C.

O programa deve ler um nome de arquivo da entrada padrão, e aplicar a cifra de César em seu conteúdo, escrevendo o resultado num arquivo cujo nome é "cifrado". Suponha que no arquivo de entrada só existem letras maiúsculas sem qualquer tipo de acentos, além espaços e finais de linha. Os caracteres brancos não podem ser modificados.

Entradas:

Nome de um arquivo a ser lido (não há espaços no nome).

Conteúdo do arquivo informado.

Saída:

Um arquivo cujo nome é "cifrado", contendo o resultado da aplicação da cifra ao conteúdo do arquivo da entrada.

Link para o comentário
Compartilhar em outros sites

  • 4 anos depois...

@yendvsb @HelpMePlease
 

Em 09/08/2016 às 11:41, HelpMePlease disse:

Entradas:

Nome de um arquivo a ser lido (não há espaços no nome).

Conteúdo do arquivo informado.

Saída:

Um arquivo cujo nome é "cifrado", contendo o resultado da aplicação da cifra ao conteúdo do arquivo da entrada.

 

Quase certamente não é isso...

 

Em 09/08/2016 às 11:41, HelpMePlease disse:

O programa deve ler um nome de arquivo da entrada padrão, e aplicar a cifra de César em seu conteúdo, escrevendo o resultado num arquivo cujo nome é "cifrado". Suponha que no arquivo de entrada só existem letras maiúsculas sem qualquer tipo de acentos, além espaços e finais de linha. Os caracteres brancos não podem ser modificados.

 

Mas é isso acima. :) ou quase

 

A saída é um arquivo codificado por esse método. Não é o nome dele que é cifrado mas sim o conteúdo. O enunciado não diz o que fazer com a saída. O normal seria dar o nome do arquivo de saída, certo? 

 

Um filtro: le o arquivo e gera o outro  cifrado...

 

A tal cifra de césar é como um carrossel de letras e o número de posições é escolhido por algum critério, e as letras na cifra podem não estar assim na ordem. Esse caso de 3 posições e as letras na ordem alfabética é uma simplificação apenas.

 

Entrar com o conteúdo do arquivo é trivial. Isso é muito simples em C.

 

Lendo um arquivo, o simples:

 

O programa a seguir lê um arquivo e mostra na saída as linhas, numeradas a partir de um e com o tamanho em caracteres. Podia ter umas 10 linhas se não permitisse passar o nome do arquivo e se não testasse nada.

 

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

int main(int argc, char** argv)
{
    if ( argc < 2 ) // nao veio o nome do arquivo na linha de comando
    {   printf("faltou o nome do arquivo: escreva 'pgm nome'!\n");
        return -1;
    };
    FILE* o_arquivo = fopen ( argv[1], "r" );
    if ( o_arquivo == NULL ) // erro!
    {   printf("Erro tentando abrir \"%s\"\n", argv[1] );
        return -2;
    };  
    printf("Lendo \"%s\"\n\n\n", argv[1] );
    char linha[256];
    int n_linha = 1;
    char* p = NULL;
    p = fgets(linha,256,o_arquivo); // primeira linha
    while ( p != NULL) // leu algo?
    {   printf("%12d: [%4zd] > %s", n_linha++, strlen(linha), linha );
        p = fgets(linha,256,o_arquivo);
    }
    return 0;
};  // main()

 

A saída

 

Abaixo a saída da compilação e da execução do programa, sem passar o arquivo, passando um arquivo inexistente ou lendo o próprio programa para testar ;)

 

 

teste$ gcc -o fread -Wall -O3 fread.c
teste$ ./fread
faltou o nome do arquivo: escreva 'pgm nome'!
teste$ ./fread xez
Erro tentando abrir "xez"
teste$ ./fread fread.c
Lendo "fread.c"


           1: [  19] > #include <stdio.h>
           2: [  20] > #include <string.h>
           3: [   1] > 
           4: [  32] > int main(int argc, char** argv)
           5: [   2] > {
           6: [  70] >     if ( argc < 2 ) // nao veio o nome do arquivo na linha de comando
           7: [  67] >     {   printf("faltou o nome do arquivo: escreva 'pgm nome'!\n");
           8: [  19] >         return -1;
           9: [   7] >     };
          10: [  46] >     FILE* o_arquivo = fopen ( argv[1], "r" );
          11: [  38] >     if ( o_arquivo == NULL ) // erro!
          12: [  58] >     {   printf("Erro tentando abrir \"%s\"\n", argv[1] );
          13: [  19] >         return -2;
          14: [   9] >     };  
          15: [  44] >     printf("Lendo \"%s\"\n\n\n", argv[1] );
          16: [  21] >     char linha[256];
          17: [  21] >     int n_linha = 1;
          18: [  20] >     char* p = NULL;
          19: [  54] >     p = fgets(linha,256,o_arquivo); // primeira linha
          20: [  36] >     while ( p != NULL) // leu algo?
          21: [  71] >     {   printf("%12d: [%4zd] > %s", n_linha++, strlen(linha), linha );
          22: [  40] >         p = fgets(linha,256,o_arquivo);
          23: [   6] >     }
          24: [  14] >     return 0;
          25: [  14] > };  // main()
teste$ 

 

Um mínimo: le "entrada.txt" e mostra na tela

 

#include <stdio.h>
int main(void)
{   FILE* o_arquivo = fopen ( "entrada.txt", "r" );
    char linha[256];
    char* p = NULL;
    p = fgets(linha,256,o_arquivo); // primeira linha
    while ( p != NULL) printf("%s", linha ), p = fgets(linha,256,o_arquivo);
    return 0;
};  // main()

 

Só pra mostrar como consumir os dados...

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

@arfneto  meu código até agora:

#include <iostream>
#include <fstream>
#include <string>
using namespace std;


int main (){
	string v[26] = {"A", "B", "C", "D", "E" ,"F" ,"G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"};
	
	string nomearq;
	cin >> nomearq;
	ofstream arquivoS(nomearq.c_str());
	
	arquivoS << "GALLIA EST OMNIS DIVISA IN PARTES TRES QUARUM UNAM INCOLUNT BELGAE" << endl;
	
	
	string c[200];
	
	ofstream arquivo;
	arquivo.open("cifrado.txt");
	
	
	if(arquivoS){
		while(arquivoS >> c){
			for(int j=0; j < 200; j++){
				for(int i=0; i<26; i++){
					if(c[j] == v[i]){
						if(c[j] == "X"){
						arquivo << "A";
					    }
					    if(c[j] == "Y"){
						arquivo << "B";
					    }
					    if(c[j] == "Z"){
						arquivo << "C";
					    }
					    if(c[j] == " "){
						arquivo << " ";
					    }else{
						arquivo << v[i +3];
					    }
			        }
			    }
			}
	    }
	}else{
		cout << "não foi possivel abrir o arquivo";
	}
	
	
	string letra[200];
	
	while(arquivo){
		cout << letra << endl;
		}
		
	arquivo.close();

	

	arquivoE.close();

	
	return 0;
	}

ainda não coloquei um cin para pegar o conteúdo do primeiro arquivo, só tô testando se o "codificador"/filtro tá dando certo e aparentemente não está. Você poderia me dizer onde estou errando?

Link para o comentário
Compartilhar em outros sites

string v[26] =
{
  "A", "B", "C", "D", "E" ,"F" ,"G", "H", "I", "J",
  "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",
  "U", "V", "W", "X", "Y", "Z"
};
	

 

Na prática é comum a empresa ou a escola ter regras para tudo nos programas, e isso inclui o alinhamento do código e até o número máximo de colunas e regras para os nomes das variáveis. :) Mas enquanto você pode escolher talvez possa usar algo mais legível. Tenho dificuldade em entender assim e com essa nomenclatura.

 

Em geral a largura das linhas é limitada, por exemplo. Veja a diferença na declaração de v[ ] acima...

 

Sobre isso, porque um vetor de 26 strings de uma letra e não uma string de 26 letras? Parece tão mais simples.

 

Seu programa parece muito, mas muito complicado. Sequer precisa desses 26 vetores que poderiam ser um só.

 

Está certo de que não precisa tratar as letras minúsculas?

 

Você vai fazer os mesmos testes para todas as letras do arquivo de entrada, e então provavelmente não há vantagem em ler linhas e ficar testando SEMPRE a mesma coisa. E como a distância está fixada em 3 letras podia estar tudo resolvido se lesse a entrada letra a letra e gravasse na saída conforme uma tabela...

 

 

  • Curtir 2
Link para o comentário
Compartilhar em outros sites

@arfneto  consegui simplificar bem o código, o único problema agora é que não dá para pegar os espaços :(

#include <iostream>
#include <fstream>
#include <string>
using namespace std;


int main (){

	string nomearq;
	cin >> nomearq;
	ifstream arquivoS(nomearq.c_str());
	
	
	char c;
	ofstream arquivo("cifrado");
	
	
	if(arquivoS){
		while(arquivoS >> c){
					if(c != ' '){
						arquivo << (char)(c + 3);
					}
					else{
						arquivo << c;
					}
		}
	}
		
	return 0;
	}

o arquivoS de entrada :

GALLIA EST OMNIS DIVISA IN PARTES TRES QUARUM UNAM INCOLUNT BELGAE

 

e o arquivo "cifrado" fica:

JDOOLDHVWRPQLVGLYLVDLQSDUWHVWUHVTXDUXPXQDPLQFROXQWEHOJDH

Link para o comentário
Compartilhar em outros sites

9 horas atrás, yendvsb disse:

consegui simplificar bem o código, o único problema agora é que não dá para pegar os espaços

 

Não adianta apenas somar 3 no código... 'Z'  deve virar ' C' , tem que "dar a volta" na tabela

 

Ao implementar essas coisas faça o simples: escreva as duas funções, crypt() e decryot(), um par para strings para testar e um par para fazer o mesmo com o arquivo, que é o objetivo afinal.

 

Se avança 3 para codificar volta 3 para decodificar, o simples. E ao chamar duas vezes é claro que tem que restaurar a string original... E assim testa seu programa...

 

Como fez está errado :( . A analogia disso é com um carrossel de letras.

 

Pense nisso: 'A' vira ' D' , 'Z' vira 'C' . E um '4' vira... '4' 

 

Há muitas maneiras de fazer isso, mas o simples é não testar NADA. nem if nem contas, apenas uma tabela

 

Veja essa classe:

 

#include <iostream>
using namespace std;
class Cesar
{
private:
    //                       "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    const char*   crypt_AZ = "DEFGHIJKLMNOPQRSTUVWXYZABC";
    const char* decrypt_AZ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    //                       "abcdefghijklmnopqrstuvwxyz"
    const char*   crypt_az = "defghijklmnopqrstuvwxyzabc";
    const char* decrypt_az = "abcdefghijklmnopqrstuvwxyz";
public:
    string cs_crypt(string linha);
    string cs_decrypt(string linha);
};

 

Essa é a maneira trivial de fazer isso. mas é também a menos eficiente. 

 

Um teste óbvio seria

 

#include <iostream>

#include "Cesar.h"
using namespace std;
int main(void)
{
	Cesar cesar;
	string teste{ "\
ABCDEFGHIJKLMNOPQRSTUVWXYZ \
abcdefghijklmnopqrstuvwxyz \
123456789 ,..;()" };
	cout << "Teste: \"" << teste << "\"\n";
	cout << "Teste: \"" << cesar.cs_crypt(teste) << "\"\n";
	cout << "Teste: \"" << cesar.cs_decrypt(teste) << "\"\n";
	return 0;
}

 

porque é mais fácil de conferir quando já sabe o que esperar... E deve sair

 

Teste: "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 123456789 ,..;()"
Teste: "DEFGHIJKLMNOPQRSTUVWXYZABC defghijklmnopqrstuvwxyzabc 123456789 ,..;()"
Teste: "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz 123456789 ,..;()"

 


São 26 letras, a da posição 0 vai virar a da posição 3. Mas a da posição 25 vai virar a da posição 25 vai virar a da posição 2: A ==> D, e Z ==> C. É claro que o tamanho do salto --- 3 --- devia ser um parâmetro, e o resultado é calculado a partir do resto da divisão por 26... Módulo 26, no popular. 
 

 0  vira ( 0 + 3) % 26 = 3 e explica o 'A' virar ´D´
25  vira (25 + 3) % 26 = 2 e explica o 'Z" virar 'C' 


e por isso eu disse que não precisa de vetor nenhum.

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Eis uma classe que deve servir para a tal cifra de Cesar:
 


#include <iostream>
using namespace std;
class Cesar
{
    private:
        char        distancia;
        char        tbl_crypt[256];
        char        tbl_decrypt[256];
    public:
        Cesar(int distancia);
        Cesar() : Cesar(3) {};
        string  cs_crypt(string linha);
        string  cs_decrypt(string linha);
        int     cs_crypt_file(string entrada, string saida);
        int     cs_decrypt_file(string entrada, string saida);
};


 

distancia é o número de posições que anda para frente para criptografar e para trás para descriptografar. tbl_crypt[] tem a letra resultante para codificar um valor, tbl_decrypt[] tem o valor contrário. Ao criar uma cifra se não declarar um "avanço" a classe assume 3, como no enunciado. Ou seja
 

    Cesar    cesar; // assume 3 "casas" 
    Cesar    igual(3); // 3 casa, da na mesma que igual
    Cesar    outro_cesar(12); // usa doze letras adiante


cs_crypt() / cs_decrypt()     tratam strings

cs_crypt_file() /cs_decrypt_file()  tratam arquivos inteiros

Como os valores são constantes, assim que definido o avanço só é preciso fazer isso (definir as tabelas) uma vez e é muito, mas muito mais rápido que ficar comparando letra a letra. No exemplo "A" sempre vira "D" e para decodificar "D" sempre vira "A" por exemplo.

 

Eis um exemplo de como construir as duas tabelas:

 

Cesar::Cesar(int distancia) : distancia(distancia)
{
    // preenche as tabelas de acordo com a 'distancia'
    // para criptografar
    for (auto i = 0; i < 256; i += 1)
    {
        if ((i >= 'A') && (i <= 'Z'))
        {
            tbl_crypt[i] = ((i - 'A' + distancia) % 26) + 'A';
            tbl_decrypt[i] = ((i - 'A' - distancia + 26) % 26) + 'A';
        }
        else
        {
            if ((i >= 'a') && (i <= 'z'))
            {
                tbl_crypt[i] = ((i - 'a' + distancia) % 26) + 'a';
                tbl_decrypt[i] = ((i - 'a' - distancia + 26) % 26) + 'a';
            }
            else
            {
                tbl_crypt[i] = i;
                tbl_decrypt[i] = i;
            }
        };  // if()
    };  // for()
};

 

E é mesmo só isso. Codificar fica bem mais fácil, por exemplo:

 

string Cesar::cs_crypt(string linha)
{
    string res{};
    for (auto letra : linha) res += tbl_crypt[letra];
    return res;
};

 

E é mesmo só isso: tudo já foi acertado na construção. E funciona para qualquer valor e não só 3 casas.

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...