Убрать метку порядка байтов из строки в C # - PullRequest
39 голосов
/ 23 августа 2009

Я читал похожие посты по этому вопросу, и они не отвечают на мой вопрос.

В C # у меня есть строка, которую я получаю из WebClient.DownloadString. Я попытался установить client.Encoding в новое UTF8Encoding (false), но это не имело никакого значения - я все равно получаю метку порядка байтов для UTF-8 в начале строки результата. Мне нужно удалить это (чтобы проанализировать полученный XML с помощью LINQ) и сделать это в памяти.

Итак, у меня есть строка, которая начинается с \ x00EF \ x00BB \ x00BF, и я хочу удалить ее, если она существует. Прямо сейчас я использую

if (xml.StartsWith(ByteOrderMarkUtf8))
{
    xml = xml.Remove(0, ByteOrderMarkUtf8.Length);
}

но это просто неправильно. Я пробовал все виды кода с потоками, GetBytes и кодировками, и ничего не работает. Может ли кто-нибудь предоставить «правильный» алгоритм для удаления спецификации из строки?

Спасибо!

Ответы [ 11 ]

45 голосов
/ 04 февраля 2011

У меня недавно были проблемы с обновлением .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, метод обрезает все пробельные символы Unicode (то есть символы, которые выдают истинное возвращаемое значение, когда они передаются методу Char.IsWhiteSpace). Из-за этого изменения метод Trim в .NET Framework 3.5 SP1 и более ранних версиях удаляет два символа, ZERO WIDTH SPACE (U + 200B) и ZERO WIDTH NO-BREAK SPACE (U + FEFF), которые метод Trim в. NET Framework 4 и более поздних версий не удаляет. Кроме того, метод Trim в .NET Framework 3.5 с пакетом обновления 1 (SP1) и более ранних версиях не обрезает три символа пробела Юникода: монгольский разделитель гласных (U + 180E), узкий пробел (U + 202F) и срединное математическое пространство (U + 205F).

41 голосов
/ 23 августа 2009

У меня были неверные тестовые данные, что вызвало у меня некоторую путаницу. На основании Как избежать отключения по спецификации 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 по-прежнему не будет читать эту строку. Это самая чистая версия, с которой я столкнулся на сегодняшний день.

29 голосов
/ 19 июля 2010

Это также работает

int index = xmlResponse.IndexOf('<');
if (index > 0)
{
    xmlResponse = xmlResponse.Substring(index, xmlResponse.Length - index);
}
19 голосов
/ 23 августа 2009

Если переменная xml имеет тип string, вы уже сделали что-то не так - в символьной строке спецификация должна быть представлена ​​не как три отдельных символа, а как одна кодовая точка. Вместо использования DownloadString используйте DownloadData и анализируйте байтовые массивы. Синтаксический анализатор XML должен распознавать саму спецификацию и пропускать ее (за исключением автоматического определения кодировки документа как UTF-8).

12 голосов
/ 25 марта 2013

Быстрый и простой способ удалить его из строки:

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);
10 голосов
/ 28 октября 2010

У меня была очень похожая проблема (мне нужно было проанализировать 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);
8 голосов
/ 21 февраля 2011

Я написал следующий пост после того, как наткнулся на эту проблему.

По сути, вместо чтения в необработанных байтах содержимого файла с использованием класса BinaryReader я использую класс StreamReader с определенным конструктором, который автоматически удаляет символ метки порядка байтов из текстовых данных, которые я пытаюсь получить.

5 голосов
/ 23 августа 2009

Передача байтового буфера (через DownloadData) в string Encoding.UTF8.GetString(byte[]), чтобы получить строку, а не загружать буфер как строку. Вероятно, у вас больше проблем с вашим текущим методом, чем просто обрезка метки порядка байтов. Если вы правильно не расшифруете его, как я предлагаю здесь, символы юникода, вероятно, будут неправильно интерпретированы, что приведет к повреждению строки.

Редактировать : Ответ Мартина лучше, так как он позволяет избежать выделения всей строки для XML, которую все равно необходимо проанализировать в любом случае. Ответ, который я дал лучше всего, относится к общим строкам, которые не нужно анализировать как XML.

3 голосов
/ 21 марта 2015

Я столкнулся с этим, когда у меня был закодированный файл 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;
}
2 голосов
/ 27 февраля 2019

Конечно, лучше, если вы можете удалить его, пока он находится на уровне байтового массива, чтобы избежать нежелательных подстрок / распределений. Но если у вас уже есть строка, это, возможно, самый простой и эффективный способ справиться с этим.

Использование:

            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;
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...