Быстрый способ для String.Except, как List.Except - PullRequest
0 голосов
/ 10 января 2019

Как и в новых .NET 6, 7 и т. Д., У нас есть класс Except для списков.

List<int> A = new List<int>();
List<int> B = new List<int>();
List<int> C = A.Except(B).ToList();

Мой вопрос: как лучше всего использовать строковую версию того же класса:

string A = "<div><p>One</p><p>Two</p></div>";
string B = "<div><p>One</p><p>Two</p><p>Three</p></div>";
string C = A.Except(B).ToString();

Получение результата = <p>Three</p>

Вместо этого я получаю:

System.Linq.Enumerable+<ExceptIterator>d__73`1[System.Char]

Что я делаю не так?

EDIT:

Просто используя самую большую строку для За исключением самой маленькой строки, меняя порядок массива:

string C = B.Except(A);

и используя: Ника 1025 * new string(C.ToArray()); дает мне:

hr

Немного неожиданный результат после использования реверса.

Ответы [ 4 ]

0 голосов
/ 10 января 2019

В вашем решении есть две проблемы.

Поведение ToString ()

При выполнении .ToString () в IEnumerable он всегда выводит тип. Это связано с тем, что IEnumerable не переопределяет поведение ToString (). См. ToString для получения дополнительной информации об этом.
Если вы хотите преобразовать IEnumerable (возвращаемый тип Except) в строку, вам нужно будет сделать

var C = new string(A.Except(B));


Поведение А. За исключением (B)

Метод Except не совсем работает так, как вы думаете.

Возьмем для примера следующий код:

var a = new List<int> { 1, 2, 3 };
var b = new List<int> { 2, 3, 4 };
var c = a.Except(b);

Результатом этого будет {1}. Что эффективно делает метод, так это возвращает новое перечисление всех целых чисел, присутствующих в a, но не в b.

Теперь строки - это просто перечисление символов - точнее, ваш

var A = "<div><p>One</p><p>Two</p></div>";

с точки зрения LINQ, эквивалентно

var A = new List<char> { '<', 'd', 'i', 'v', '>', ..., '<', '/', 'd', 'i', 'v', '>' };

То же самое относится и к B.

Таким образом, когда вы выполняете A.Except (B), LINQ на самом деле будет проходить через каждый символ и смотреть, сможет ли он найти его в B. Если это произойдет, он не окажется в наборе результатов. Теперь, поскольку все символы в A также присутствуют в B, вы получите пустую строку. Чтобы увидеть, что это действительно так, слегка измените A, чтобы он содержал символ, которого нет в B:

string A = "<div><p>One</p><p>Two</p></div>ApplePie";

Если вы сейчас делаете

string A = "<div><p>One</p><p>Two</p></div>ApplePie";
string B = "<div><p>One</p><p>Two</p><p>Three</p></div>";
string C = new string(A.Except(B).ToArray());

вы получите "AlP".

Решение

На мой взгляд, лучший способ сделать что-либо кроме анализа - это проанализировать ваши строки, преобразовать их в объекты, а затем выполнить исключение. Ни один встроенный алгоритм не может сказать, что ваши строки действительно структурированы и как их различать. И в качестве рабочего решения, используя HtmlAgilityPack (пакет nuget)

var docB = new HtmlDocument();
docB.LoadHtml(B);

var docA = new HtmlDocument();
docA.LoadHtml(A);
var nodes = docB.DocumentNode.FirstChild.Descendants("p").Select(node => node.InnerHtml)
    .Except(docA.DocumentNode.FirstChild.ChildNodes.Select(node => node.InnerHtml));
// take note that we are actually doing whatIsInB.Except(whatIsInA), since doing the reverse would result in nothing. There is no &lt;p&gt; in A that is not also present in B

var result = string.Join(Environment.NewLine, nodes); // will resut in "Three"
var otherResult = $"<p>{result}</p>"; // "<p>Three</p>"

Я позволю вам сделать более общий подход:)
Но идея в том, что если вы хотите работать иначе, чем ожидаете, вам придется попросить его работать со строками, а не с символами.

Делаете ли вы синтаксический анализ, необходимый для извлечения компонентов вашей строки (элементы

в этом примере), используя HtmlAgilityPack или Regex, как это предлагается в других решениях, полностью зависит от вас.

0 голосов
/ 10 января 2019

Использование string C = new string (A.Except(B).ToArray());

0 голосов
/ 10 января 2019

Требуемое значение не равно Except, потому что это операция набора, известная как разница набора или относительное дополнение , в которой вы говорите, что хотите, чтобы элементы из набора отсутствовали в другом .

Вы можете достичь ожидаемого результата с помощью групп регулярных выражений вместо:

using System;
using System.Text.RegularExpressions;

class Program
{
    static void Main()
    {
        // Input string.
        string input = "<div><p>One</p><p>Two</p><p>Three</p></div>";

        // Use named group in regular expression.
        Regex expression = new Regex(@"^<div><p>One</p><p>Two</p>(?<middle>[<>/\w]+)</div>$");

        // See if we matched.
        Match match = expression.Match(input);
        if (match.Success)
        {
            // Get group by name.
            string result = match.Groups["middle"].Value;
            Console.WriteLine("Middle: {0}", result);
        }

        // Done
        Console.ReadLine();
    }
}

С помощью регулярного выражения ^<div><p>One</p><p>Two</p>(?<middle>[<>/\w]+)</div>$ вы говорите, что строка должна начинаться ( ^ ) с <div><p>One</p><p>Two</p> и заканчиваться ( $ ) с </div>, и что угодно между которыми содержится <</strong>, > , / или любой буквенно-цифровой символ ( \ w ) более одного раза ( + ), будет добавлен в группу с именем middle .

Однако я бы не рекомендовал вам попытаться проанализировать HTML с помощью регулярного выражения ...

0 голосов
/ 10 января 2019

Когда вы используете метод расширения Except (), тип возвращаемого значения - List of Char.

Документация

Кроме того, A.Excepts (B) никогда не выдаст то, что вы хотите, потому что преобразует строку в массивы char. Таким образом, он удалит каждый символ из A, который присутствует в B.

Для этого нужно придумать другой алгоритм.

...