C#:Циклически перебирайте кодировки
-
20-08-2019 - |
Вопрос
Я читаю файлы в различных форматах и языках, и в настоящее время я использую небольшую библиотеку кодирования, чтобы попытаться определить правильную кодировку (http://www.codeproject.com/KB/recipes/DetectEncoding.aspx).
Это довольно хорошо, но все равно иногда промахивается.(Многоязычные файлы)
Большинство моих потенциальных пользователей очень слабо разбираются в кодировке (лучшее, на что я могу надеяться, это "это как-то связано с символами") и вряд ли смогут выбрать правильную кодировку в списке, поэтому я хотел бы позволить им перебирать разные кодировки, пока не будет найдена нужная, просто нажав на кнопку.
Проблемы с дисплеем?Нажмите здесь, чтобы попробовать другую кодировку!(Ну, в любом случае, такова концепция)
Каков был бы наилучший способ реализовать что-то подобное?
Редактировать:Похоже, я недостаточно ясно выразился.Под "циклическим перебором кодировок" я не имею в виду "как перебирать кодировки?".
Я имел в виду "как позволить пользователю последовательно пробовать разные кодировки без перезагрузки файла?"
Идея больше похожа на эту:Допустим, файл загружен с неправильной кодировкой.Отображаются какие-то странные символы.Пользователь нажимал бы кнопку "Следующая кодировка" или "предыдущая кодировка", и строка была бы преобразована в другую кодировку.Пользователю просто нужно продолжать нажимать до тех пор, пока не будет найдена нужная кодировка.(какая бы кодировка ни выглядела хорошо для пользователя, она будет работать нормально).Пока пользователь может нажать "далее", у него есть разумные шансы решить свою проблему.
То, что я нашел до сих пор, включает преобразование строки в байты с использованием текущей кодировки, затем преобразование байтов в следующую кодировку, преобразование этих байтов в символы, затем преобразование символа в строку...Выполнимо, но мне интересно, нет ли более простого способа сделать это.
Например, если бы существовал метод, который считывал бы строку и возвращал ее, используя другую кодировку, что-то вроде "render (строка, кодировка)".
Большое спасибо за ответы!
Решение
Считайте файл в виде байтов и затем используйте кодировку.Метод getString.
byte[] data = System.IO.File.ReadAllBytes(path);
Console.WriteLine(Encoding.UTF8.GetString(data));
Console.WriteLine(Encoding.UTF7.GetString(data));
Console.WriteLine(Encoding.ASCII.GetString(data));
Таким образом, вам нужно загрузить файл только один раз.Вы можете использовать любую кодировку, основанную на исходных байтах файла.Пользователь может выбрать правильный вариант, а вы можете использовать результат кодирования.GetEncoding(...).getString(данные) для дальнейшей обработки.
Другие советы
(удален оригинальный ответ после обновления вопроса)
Например, если бы существовал метод который считывал бы строку и возвращал ее используя другую кодировку, что-то вроде "render(строка, кодировка)".
Я не думаю, что вы можете повторно использовать строковые данные.Дело в том, что:если кодировка была неправильной, эту строку можно считать поврежденной.Он очень легко может содержать тарабарщину среди вероятных символов.В частности, многие кодировки могут допускать наличие / отсутствие спецификации / преамбулы, но не могли бы вы перекодировать их заново?без этого?
Если вы готовы рискнуть этим (я бы не стал), вы могли бы просто перекодировать свою локальную строку с последней кодировкой:
// I DON'T RECOMMEND THIS!!!!
byte[] preamble = lastEncoding.GetPreamble(),
content = lastEncoding.GetBytes(text);
byte[] raw = new byte[preamble.Length + content.Length];
Buffer.BlockCopy(preamble, 0, raw, 0, preamble.Length);
Buffer.BlockCopy(content, 0, raw, preamble.Length, content.Length);
text = nextEncoding.GetString(raw);
На самом деле, я считаю, что лучшее, что вы можете сделать, это сохранить оригинал byte[]
- продолжайте предлагать разные рендеринги (в разных кодировках), пока им что-то не понравится.Что - то вроде:
using System;
using System.IO;
using System.Text;
using System.Windows.Forms;
class MyForm : Form {
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.Run(new MyForm());
}
ComboBox encodings;
TextBox view;
Button load, next;
byte[] data = null;
void ShowData() {
if (data != null && encodings.SelectedIndex >= 0) {
try {
Encoding enc = Encoding.GetEncoding(
(string)encodings.SelectedValue);
view.Text = enc.GetString(data);
} catch (Exception ex) {
view.Text = ex.ToString();
}
}
}
public MyForm() {
load = new Button();
load.Text = "Open...";
load.Dock = DockStyle.Bottom;
Controls.Add(load);
next = new Button();
next.Text = "Next...";
next.Dock = DockStyle.Bottom;
Controls.Add(next);
view = new TextBox();
view.ReadOnly = true;
view.Dock = DockStyle.Fill;
view.Multiline = true;
Controls.Add(view);
encodings = new ComboBox();
encodings.Dock = DockStyle.Bottom;
encodings.DropDownStyle = ComboBoxStyle.DropDown;
encodings.DataSource = Encoding.GetEncodings();
encodings.DisplayMember = "DisplayName";
encodings.ValueMember = "Name";
Controls.Add(encodings);
next.Click += delegate { encodings.SelectedIndex++; };
encodings.SelectedValueChanged += delegate { ShowData(); };
load.Click += delegate {
using (OpenFileDialog dlg = new OpenFileDialog()) {
if (dlg.ShowDialog(this)==DialogResult.OK) {
data = File.ReadAllBytes(dlg.FileName);
Text = dlg.FileName;
ShowData();
}
}
};
}
}
Не могли бы вы разрешить пользователю вводить некоторые слова (со "специальными" символами), которые должны встречаться в файле?
Вы можете самостоятельно выполнить поиск по всем кодировкам, чтобы узнать, присутствуют ли эти слова.
Остерегайтесь печально известных 'Ошибка в блокноте'.Однако он будет кусать тебя, что бы ты ни пытался...Вы можете найти некоторые хорошие дискуссии о кодировках и их проблемах в MSDN (и других местах).
Вы должны сохранить исходные данные в виде массива байтов или потока памяти, который затем вы можете преобразовать в новую кодировку, поскольку после того, как вы уже преобразовали свои данные в строку, вы не сможете надежно вернуться к исходному представлению.
Как насчет чего-то вроде этого:
public string LoadFile(string path)
{
stream = GetMemoryStream(path);
string output = TryEncoding(Encoding.UTF8);
}
public string TryEncoding(Encoding e)
{
stream.Seek(0, SeekOrigin.Begin)
StreamReader reader = new StreamReader(stream, e);
return reader.ReadToEnd();
}
private MemoryStream stream = null;
private MemorySteam GetMemoryStream(string path)
{
byte[] buffer = System.IO.File.ReadAllBytes(path);
return new MemoryStream(buffer);
}
Используйте LoadFile с первой попытки;затем используйте TryEncoding впоследствии.