Strip Byte Order Mark от строки в С#
Я читал подобные сообщения по этому вопросу, и они не отвечают на мой вопрос.
В С# у меня есть строка, которую я получаю из WebClient.DownloadString. Я попытался настроить client.Encoding на новый UTF8Encoding (false), но это не имело никакого значения - я все еще заканчиваю знаком байтового порядка для UTF-8 в начале строки результата. Мне нужно удалить это (проанализировать полученный XML с помощью LINQ) и сделать это в памяти.
Итак, у меня есть строка, начинающаяся с \x00EF\x00BB\x00BF, и я хочу удалить ее, если она существует. Прямо сейчас я использую
if (xml.StartsWith(ByteOrderMarkUtf8))
{
xml = xml.Remove(0, ByteOrderMarkUtf8.Length);
}
но это просто неправильно. Я пробовал все виды кода с потоками, GetBytes и кодировками, и ничего не работает. Может ли кто-нибудь предоставить "правильный" алгоритм для выделения спецификации из строки?
Спасибо!
Ответы
Ответ 1
Если переменная xml имеет строку типа, вы уже сделали что-то неправильно - в символьной строке спецификация не должна быть представлена как три отдельных символа, а как единая кодовая точка. Вместо использования DownloadString используйте DownloadData и вместо этого проанализируйте байтовые массивы. Парсер XML должен сам распознать спецификацию и пропустить ее (за исключением автоматического обнаружения кодировки документа как UTF-8).
Ответ 2
Недавно у меня были проблемы с обновлением .net 4, но до этого простой ответ был
String.Trim()
удаляет спецификацию вверх до .net 3.5
Однако в .net 4 вам нужно немного изменить его.
String.Trim(new char[]{'\uFEFF'});
Это также избавит вас от знака порядка байтов, хотя вы также можете удалить ZERO WIDTH SPACE U + 200B
String.Trim(new char[]{'\uFEFF','\u200B'});
Это вы также можете использовать для удаления других нежелательных символов
Дополнительная информация из
http://msdn.microsoft.com/en-us/library/t97s7bs3.aspx
В .NET Framework 3.5 с пакетом обновления 1 (SP1) и более ранних версиях поддерживается внутренний список символов пробела, которые этот метод обрезает. Начиная с .NET Framework 4, метод обрезает все символы пробела Юникода (то есть символы, которые генерируют истинное возвращаемое значение, когда они передаются методу Char.IsWhiteSpace). Из-за этого изменения метод Trim в .NET Framework 3.5 с пакетом обновления 1 (SP1) и более ранних версиях удаляет два символа: ZERO WIDTH SPACE (U + 200B) и ZERO WIDTH NO-BREAK SPACE (U + FEFF), что метод Trim в .NET Framework 4 и более поздние версии не удаляются. Кроме того, метод Trim в .NET Framework 3.5 с пакетом обновления 1 (SP1) и более ранних версиях не обрезает три символа пробела Unicode: MONGOLIAN VOWEL SEPARATOR (U + 180E), NARROW NO-BREAK SPACE (U + 202F) и MEDIUM MATHEMATICAL SPACE (U + 205F).
Ответ 3
У меня были некорректные тестовые данные, которые вызвали у меня некоторую путаницу. Основываясь на Как избежать опрокидывания спецификации UTF-8 при чтении файлов, я обнаружил, что это сработало:
private readonly string _byteOrderMarkUtf8 =
Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
public string GetXmlResponse(Uri resource)
{
string xml;
using (var client = new WebClient())
{
client.Encoding = Encoding.UTF8;
xml = client.DownloadString(resource);
}
if (xml.StartsWith(_byteOrderMarkUtf8, StringComparison.Ordinal))
{
xml = xml.Remove(0, _byteOrderMarkUtf8.Length);
}
return xml;
}
Настройка свойства Кодирование клиента правильно уменьшает спецификацию до одного символа. Однако XDocument.Parse все равно не будет читать эту строку. Это самая чистая версия, с которой я пришел на сегодняшний день.
Ответ 4
Это также работает
int index = xmlResponse.IndexOf('<');
if (index > 0)
{
xmlResponse = xmlResponse.Substring(index, xmlResponse.Length - index);
}
Ответ 5
Быстрый и простой способ удалить его directyl из строки:
private static string RemoveBom(string p)
{
string BOMMarkUtf8 = Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble());
if (p.StartsWith(BOMMarkUtf8))
p = p.Remove(0, BOMMarkUtf8.Length);
return p.Replace("\0", "");
}
Как использовать:
string yourCleanString=RemoveBom(yourBOMString);
Ответ 6
У меня была очень похожая проблема (мне нужно было разобрать XML-документ, представленный как массив байтов, в начале которого был отмечен порядок байтов). Я использовал один из комментариев Мартина о его ответе, чтобы прийти к решению. Я взял массив байтов, который у меня был (вместо того, чтобы преобразовать его в строку) и создал с ним объект MemoryStream
. Затем я передал его XDocument.Load
, который работал как шарм. Например, скажем, что xmlBytes
содержит ваш XML в кодировке UTF8 с байтовой меткой в начале его. Тогда это будет код для решения проблемы:
var stream = new MemoryStream(xmlBytes);
var document = XDocument.Load(stream);
Это просто.
Если начать со строки, ее все равно будет легко сделать (предположим, что xml
- это ваша строка, содержащая XML с байтом порядка байтов):
var bytes = Encoding.UTF8.GetBytes(xml);
var stream = new MemoryStream(bytes);
var document = XDocument.Load(stream);
Ответ 7
Я написал следующий пост, после того как встретился с этой проблемой.
По существу вместо чтения в необработанных байтах содержимого файла с использованием класса BinaryReader я использую класс StreamReader со специальным конструктором, который автоматически удаляет символ маркера порядка байтов из текстовых данных, которые я пытаюсь извлечь.
Ответ 8
Передайте байтовый буфер (через DownloadData) в string Encoding.UTF8.GetString(byte[])
, чтобы получить строку, а не загружать буфер AS как строку. Вероятно, у вас больше проблем с вашим текущим методом, чем просто обрезка значка порядка байтов. Если вы не правильно декодируете его, как я предлагаю здесь, символы юникода, вероятно, будут неверно истолкованы, что приведет к поврежденной строке.
Изменить. Мартин ответ лучше, поскольку он позволяет избежать выделения всей строки для XML, которая все равно нуждается в анализе. Ответ, который я дал лучше всего, относится к общим строкам, которые не нужно анализировать как XML.
Ответ 9
Я столкнулся с этим, когда у меня был файл с кодировкой base-64 для преобразования в строку. Хотя я мог бы сохранить его в файле, а затем прочитать его правильно, вот лучшее решение, которое я мог бы придумать, чтобы перейти от byte[]
файла к строке (на основе ответа на TrueWill):
public static string GetUTF8String(byte[] data)
{
byte[] utf8Preamble = Encoding.UTF8.GetPreamble();
if (data.StartsWith(utf8Preamble))
{
return Encoding.UTF8.GetString(data, utf8Preamble.Length, data.Length - utf8Preamble.Length);
}
else
{
return Encoding.UTF8.GetString(data);
}
}
Где StartsWith(byte[])
- логическое расширение:
public static bool StartsWith(this byte[] thisArray, byte[] otherArray)
{
// Handle invalid/unexpected input
// (nulls, thisArray.Length < otherArray.Length, etc.)
for (int i = 0; i < otherArray.Length; ++i)
{
if (thisArray[i] != otherArray[i])
{
return false;
}
}
return true;
}
Ответ 10
Конечно, лучше всего, если вы можете удалить его, пока он находится на уровне байтового массива, чтобы избежать нежелательных подстрок/распределений. Но если у вас уже есть строка, это, возможно, самый простой и эффективный способ справиться с этим.
Использование:
string feed = ""; // input
bool hadBOM = FixBOMIfNeeded(ref feed);
var xElem = XElement.Parse(feed); // now does not fail
/// <summary>
/// You can get this or test it originally with: Encoding.UTF8.GetString(Encoding.UTF8.GetPreamble())[0];
/// But no need, this way we have a constant. As these three bytes '[239, 187, 191]' (a BOM) evaluate to a single C# char.
/// </summary>
public const char BOMChar = (char)65279;
public static bool FixBOMIfNeeded(ref string str)
{
if (string.IsNullOrEmpty(str))
return false;
bool hasBom = str[0] == BOMChar;
if (hasBom)
str = str.Substring(1);
return hasBom;
}
Ответ 11
StreamReader sr = new StreamReader(strFile, true);
XmlDocument xdoc = new XmlDocument();
xdoc.Load(sr);
Ответ 12
Еще один общий вариант избавления от преамбулы спецификации UTF-8:
var preamble = Encoding.UTF8.GetPreamble();
if (!functionBytes.Take(preamble.Length).SequenceEqual(preamble))
preamble = Array.Empty<Byte>();
return Encoding.UTF8.GetString(functionBytes, preamble.Length, functionBytes.Length - preamble.Length);