Показываются символы ???? при написании текста в файле на кириллице
У меня есть строка:
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 шт):
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.