Показываются символы ???? при написании текста в файле на кириллице

У меня есть строка: string text = "Тест";

Я сделал свой собственный класс, который позволяет писать и читать файл посимвольно (FileStream, StreamWriter, StreamReader) и не планировал в принципе глубоко работать с кодировкой

Индексатор для чтения:

public char this[long position]
{
    get
    {
        _stream.Position = position;
        return (char)_reader.Read();
    }
    set
    {
        _stream.Position = position;
        _writer.Write(value);
    }
}

Когда записываю всю строку в файл, там такой результат: ллЛЛѓ
Если этот текст считать с помощью Console.WriteLine, будет это: ???т?

А планируется всё-таки получить "Тест"

Инициализировал _reader и _writer с разными Encoding. Все перепробовал: ASCII, Unicode, Default, UTF8

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

Как можно сделать это правильным способом?


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

Автор решения: aepot

Position это в байтах, верно? А символ в кодировке UTF-8 сколько байт занимает?

Правильно, 1 и более байт. Кириллица гарантированно больше одного байта символ занимает, обычно два. Вот и получается, что позиция в файле к примеру 15 байт, то это 7,5 символ при условии, что перед позицией только киррилические символы. Если среди них есть например пробел, который занимает не 2 а 1 байт, то это 8 символ. То есть в многобайтовой кодировке расчитать позицию символа в файле непросуммировав длины всех предыдущих символов, условно невозможно.

Единственный вариант, при котором этот код может работать - это использование однобайтоаой кодировки, а их я знаю две: Windows-1251 и Codepage 866 (непопулярные типа KOI-8R в пример не привожу).

С другой стороны, все киррилические однобайтовые кодировки уже давно устарели и использовать их - сомнительное удовольствие.

Итого, проблемы с показанным индексатором нет при условии использования однобайтовой кодировки. В противном случае по длине байт != символ, отсюда вся математика.


Чтобы получить "Тест" в кодировке UTF-8, надо записать буквы на 0, 2, 4 и 6 позиции соответственно.

Затем переключить кодировку консоли

Console.OutputEncoding = Encoding.UT8;

И вывести байты файла. Слово "Тест" отобразится корректно.

Кстати, бывает UTF-8 с BOM и без BOM (двухбайтовый префикс кодировки в начале файла). Для StreamWriter разница есть. Последний создаётся так: new UTF8Encoding(false)

Чтобы получить нестандартную кодировку в .NET 7, нужно при старте приложения выполнить один раз

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

А затем в любое время получить кодировку

Encoding win1251 = Encoding.GetEncoding("Windows-1251");

Как можно объехать проблему: взять файл, записанный в кодировке UTF-8. Прочитать его целиком в строку.

string text = File.ReadAllText(path);

Преобразовать в массив символов

char[] chars = text.ToCharArray();

Заменить в массиве нужные символы и преобразовать обратно в строку

string result = new string(chars);

И потом записать обратно в файл целиком

File.WriteAllText(path, result);

Условно, если размер файла до 100 мегабайт, работать будет нормально.

По умолчанию все текстовые операции с файлами выполняются в кодировке UTF-8.

→ Ссылка