Почему не шифруются русские символы?

Почему данный код шифрует все символы, кроме русских?

Здесь я ищу и заменяю символ в алфавите:

private void encrypting(Path sourcePath, Path resultPath, List<Character> alphabet, int key) {
    try (FileChannel source = FileChannel.open(sourcePath, StandardOpenOption.READ);
         FileChannel result = FileChannel.open(resultPath, StandardOpenOption.WRITE)) {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (source.read(buffer) != -1) {
            buffer.flip();
            while (buffer.hasRemaining()) {
                byte b = buffer.get();
                char c = (char) b;
                int index = alphabet.indexOf(c);
                if (index != -1) {
                    int newIndex = Math.floorMod(index + key, alphabet.size());
                    char encrypted = alphabet.get(newIndex);
                    result.write(ByteBuffer.wrap(new byte[]{(byte) encrypted}));
                } else {
                    result.write(ByteBuffer.wrap(new byte[]{(byte) c}));
                }
            }
            buffer.clear();
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}

Здесь русские буквы добавляются в алфавит:

for (char i = 'А'; i <= 'Я'; i++) {
    alphabet.add(i);
}
for (char i = 'а'; i <= 'я'; i++) {
    alphabet.add(i);
}

Исходная строка, которую надо было зашифровать:

Бородино Borodino 1234567890(),?>

И результат шифрования:

Бородино%Gtwtinst%6789:;<=>5-.1ED

Почему данный код шифрует все символы, кроме русского алфавита?


Ответы (1 шт):

Автор решения: Pak Uula

В большинстве случаев буковки русского алфавита хранятся в файлах не в одном байте, а в нескольких. Правило представления буковов последовательностью байтов, называется кодировкой. В современном мире наиболее распространённой кодировкой является UTF-8.

Но к вашему и нашему счастию разработчики JRE уже позаботились о том, чтобы перекодировать из байтов в буквы и обратно. Этим занимаются классы, реализующие абстрактные классы java.io.Reader и java.io.Writer.

Поэтому вам стоит в вашем шифровальщике работать не файловыми каналами и самостоятельно реализовывать декодирование байты-символы, а воспользоваться стандартной библиотекой:

    public void encrypt(Reader in, Writer out, int key) throws IOException {
        for (int i = in.read(); i >= 0; i = in.read()) {
            char ch = (char) i;
            int index = alphabet.indexOf(ch);
            if (index != -1) {
                int newIndex = (index + key) % alphabet.size();
                char encrypted = alphabet.get(newIndex);
                out.write(encrypted);
            } else {
                out.write(ch);
            }
        }
    }

Теперь как Path в превратить в читателя-писателя:

    public void encrypt(Path sourcePath, Path resultPath, int key) throws IOException {
        var in = Files.newBufferedReader(sourcePath); // UTF-8 by default
        try {
            var out = Files.newBufferedWriter(resultPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE); // UTF-8
                                                                                                                // by
                                                                                                                // default
            try {
                encrypt(in, out, key);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }

Как шифровать:

    public static void main(String[] argv) throws Exception {
        System.out.println("Hello, world!");
        var enc = new Encrypt();
        enc.encrypt(Paths.get("in.txt"), Paths.get("out.txt"), 2);
    }

Исходный файл Бородино Borodino 1234567890 Результат: Гртржкпр Dqtqfkpq 3456789АБ2

Класс Encrypt целиком

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.LinkedList;
import java.util.List;

public class Encrypt {
    public static List<Character> initAlphabet() {
        var alphabet = new LinkedList<Character>();
        for (char i = 'A'; i <= 'Z'; i++) {
            alphabet.add(i);
        }
        for (char i = 'a'; i <= 'z'; i++) {
            alphabet.add(i);
        }
        for (char i = '0'; i <= '9'; i++) {
            alphabet.add(i);
        }
        for (char i = 'А'; i <= 'Я'; i++) {
            alphabet.add(i);
        }
        for (char i = 'а'; i <= 'я'; i++) {
            alphabet.add(i);
        }
        return alphabet;
    }

    public final List<Character> alphabet = initAlphabet();

    public void encrypt(Reader in, Writer out, int key) throws IOException {
        for (int i = in.read(); i >= 0; i = in.read()) {
            char ch = (char) i;
            int index = alphabet.indexOf(ch);
            if (index != -1) {
                int newIndex = (index + key) % alphabet.size();
                char encrypted = alphabet.get(newIndex);
                out.write(encrypted);
            } else {
                out.write(ch);
            }
        }
    }

    public void encrypt(Path sourcePath, Path resultPath, int key) throws IOException {
        var in = Files.newBufferedReader(sourcePath); // UTF-8 by default
        try {
            var out = Files.newBufferedWriter(resultPath, StandardOpenOption.CREATE, StandardOpenOption.WRITE); // UTF-8
                                                                                                                // by
                                                                                                                // default
            try {
                encrypt(in, out, key);
            } finally {
                out.close();
            }
        } finally {
            in.close();
        }
    }
}
→ Ссылка