# Problem in “encrypting” text program in Java

Posted on

#### Question :

So guys, I have a little problem with one of the workouts I have to do.

The teacher provided `.doc` with instructions for a ” Encryptor Program ” that should mask the alphabet and encrypt a text (Enigma style). See Exercise Statement:

Your program should have a method for encrypting and decrypting a document.

In both methods, the text file should be loaded and the key (integer value 2 to 5) requested from the user.

Let’s imagine that each letter has an integer value. We begin with the letter `A` , which has `0` value. Then `B` with value `1` , `C` with value `2` and so on up to the letter `Z` .

See the table below:

``````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

0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25
``````

When encrypting a document we will do the following:

We convert the letter that we want to encrypt, putting in its place and letter that is in its position + key.

For example:

We want to convert the letter `B` . Let’s assume that the key entered by the user is `3` .

`B` is in `1` position. The position of `B` + key results in `4` . Therefore, we will replace the letter `B` with the letter `E` .

If the word is `BANANA` , using the key `3` , it will be converted to `EDQDQD` .

If the value obtained by the sum of the position and the key is greater than 25 (letter Z), then it should continue at the beginning of the alphabet.

For example:
We want to convert the letter `X` . Let’s assume that the key entered by the user is `5` .

`X` is in `23` position. The position of `X` + key results in `28` . If we go back to the beginning of the alphabet, the number `28` will fall in the letter `C` . Therefore, we will replace the letter `X` with the letter `C` .

If the word is `XUXU` , using the `5` key, it will be converted to `CZCZ` .
In addition blanks should be converted to a sharp (#).
When we decrypt, we will reverse the process. “

I have already done a lot of the code, but some problems arise: in the output text, instead of ” `#` ” it breaks a line between the words.

If you have any questions regarding the clarity of my explanation of the problem, please comment.

``````import java.util.Scanner;
import java.io.File;
import java.io.PrintWriter;
import java.io.IOException;
public class trab3{

public static void main(String [] args){
inicio();
}

public static void inicio(){
try{
Scanner in = new Scanner(System.in);

System.out.println("finforme o nomedoarquivo.txt:");
String arquivo = in.nextLine();
// o arquivo de entrada deve estar previamente colocado na pasta do projeto

System.out.println("informe o nomedoarquivodesaida.txt:");
String arquivosaida = in.nextLine();
// para selecionar o nome do arquivo de saida

PrintWriter saida = new PrintWriter(arquivosaida);

int op = 0;
int chave;
do{
System.out.println("Digite 1 para criptografar o arquivo");
System.out.println("Digite 2 para descriptografar o arquivo");
op = in.nextInt();
if(op != 1 && op != 2){
System.out.println("Opção inválida.");
inicio();
} else {
System.out.println("informe a chave de 1 até 5");
chave = in.nextInt();
if(chave != 1 && chave != 2 && chave != 3 && chave != 4 && chave != 5){
System.out.println("Chave inválida");
inicio();
}
}
} while (op != 1 && op != 2);
} catch(IOException e){
System.out.println("Erro com o arquivo. Tente novamente");
inicio();
}
}

int key = chave;
if(op == 1){
}
else{
}

}
saida.close();
}

public static String criptografa(String palavra, int chave){
String alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(int i = 0; i < palavra.length(); i++){
if(palavra.indexOf(palavra.charAt(i)) == ' '){
char novaletra = '#';
}else{
int posicaodaletra = alfabeto.indexOf(palavra.charAt(i));
int novaposicao = posicaodaletra + chave;
if(novaposicao > 25){
char novaletra = alfabeto.charAt(posicaodaletra+chave-26);
}else{
char novaletra = alfabeto.charAt(novaposicao);
}

}
}
}

public static String descriptografa(String palavra , int chave){
String alfabeto = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(int i = 0; i < palavra.length(); i++){
if(palavra.indexOf(palavra.charAt(i)) == ' '){
char novaletra = '#';
}else{
int posicaodaletra = alfabeto.indexOf(palavra.charAt(i));
int novaposicao = posicaodaletra - chave;
if(novaposicao < 0){
char novaletra = alfabeto.charAt(posicaodaletra-chave+26);
}else{
char novaletra = alfabeto.charAt(novaposicao);
}

}
}
}
}
``````

Your problem is that you are using `Scanner` to read the file, not a simple `BufferedReader` or similar. The `Scanner` breaks the entry into words, so that:

• A blank space will never appear because `Scanner` does not return them (by finding one, it returns the word it just read, and calling `next` it starts reading a new word);
• You are doing `println` after converting each word, which introduces a line break between them.
• When making any reversible conversion of file contents, it is important to preserve all information, so I do not recommend using `Scanner` . I suggest replacing:

``````File arquivoDeEntrada = new File(arquivo);
``````

by:

``````File arquivoDeEntrada = new File(arquivo);
``````

(Note: in this case you can, since the file is ASCII – in practice, also take into account the character encoding – encoding – file)

And although it is not ideal (for the purpose of this exercise I believe it is ok), replace the reading of several “words” by reading entire lines of the file:

``````while(entrada.hasNext()){
...
}
``````

by

``````for ( String linha = entrada.readLine() ; linha != null ; linha = entrada.readLine() ) {
palavra = linha.toUpperCase();
...
}
``````

This solves your problem of line breaks. Detail: I do not know if it was a mistake when transcribing, but when you check for “invalid key” the `if` keys are closing in the wrong place:

``````if(chave != 1 && chave != 2 && chave != 3 && chave != 4 && chave != 5){
System.out.println("Chave inválida");
inicio(); // chama inicio() se a chave for inválida?!
}         // e NÃO CHAMA se a chave for válida?!!!
``````

Always try to indentify your code correctly, this makes it easy to see errors of this type.

• Why do you test for all values of `1` to `5` for the key? It would not be simpler to do:

``````if ( chave < 1 || 5 < chave ){
System.out.println("Chave inválida");
``````
• This algorithm ( Cifra César ) basically does the sum / subtraction module 26. The “module “, as you may have studied in math, is basically the rest of the division, which in Java can be done through the `%` operator:

``````int resto = 12 % 5; // 2
``````

It’s annoying detail that’s the negative numbers ( Java chose to make the result signal the same as the dividend sign ). To get around this, just add `26` while doing the subtraction, so it is guaranteed that the result will always be positive:

``````// criprografa
int novaposicao = (posicaodaletra + chave) % 26;

// descriptografa
int novaposicao = (posicaodaletra - chave + 26) % 26;
``````

The result will be guaranteed (assuming the handwritten letter is not invalid, type a `Ç` ) a number between `0` and `25` , eliminating the need for that `if` .

• In practice, multiple string concatenations can cause performance problems because a new string is created with each operation (since strings are immutable). For this exercise I believe it is not necessary, but get used to using `StringBuilder` whenever you need to do several successive operations with string:

``````// Em vez de:
String s = "";
s += str1;
s += str2;
s += str3;

// Faça:
StringBuilder sb = new StringBuilder()
sb.append(str1);
sb.append(str2);
sb.append(str3);
String s = sb.toString();
``````
• Just out of curiosity, in the case of letters from `A` to `Z` you do not need a string `alfabeto` to convert from / to numbers from `0` to `25` . These letters are represented in ASCII (and Unicode / UTF-16) in contiguous positions, where `A` is `65` (hexa `41` ) and `Z` is `90` (hexa `5A` ) . So with simple numeric and cast operations you can do this conversion without the need for a look-up:

``````char letra = 'B';
int chave = 3;

int posLetra = (int)letra;                // 66
int codLetra = posLetra - 0x41;           // 1
int convertida = (codLetra + chave) % 26; // 4
int posConvertida = convertida + 0x41;    // 69
char letraFinal = (char)posConvertida;    // 'E'
``````

If this is too confusing for you, ignore, the clarity of the code at first is more important.