C # Regex Split, но включает пустую строку, если не удается разделить - PullRequest
0 голосов
/ 14 января 2019

Я пытаюсь разбить строку на массив строк. Моя текущая строка выглядит следующим образом, и все это в одной строке. Он также имеет переводы строки (\ r \ n) и пробелы. Я привожу здесь более привлекательный пример.


BFFPPB14     Dark Chocolate Dried Cherries     14 oz (397g)

INGREDIENTS: DARK CHOCOLATE (SUGAR, CHOCOLATE LIQUOR, COCOA BUTTER,
ANHYDROUS MILK FAT, SOYA LECITHIN, VANILLIN [AN ARTIFICIAL FLAVOR]), DRIED 
TART CHERRIES (CHERRIES, SUGAR), GUM ARABIC, CONFECTIONER'S GLAZE.

CONTAINS: MILK, SOY

ALLERGEN INFORMATION: MAY CONTAIN TREE NUTS, PEANUTS, EGG AND 
WHEAT. 

01/11/2019

Description: Sweetened dried Montmorency cherries that are panned with dark chocolate. 

Storage Conditions: Store at ambient temperatures with a humidity less than 50%. 
Shelf Life: 9 months

Company Name

Item No.: 701804

Bulk: 415265

Supplier: Cherryland's Best

WARNING: CHERRIES MAY CONTAIN PITS

Мой Regex выглядит так

List<string> result = Regex.Split(text, @"INGREDIENTS: |CONTAINS: |ALLERGEN INFORMATION: |(\d{1,2}/\d{1,2}/\d{2,4})|Description: |Storage Conditions: |Shelf Life: |Company Name|Item No.: |Bulk: |Supplier: |WARNING: ").ToList();

Вот как выглядит результат

Примечание: первая строка - название продукта

Иногда я получаю строки, у которых нет поставщика или предупреждения, я хочу, чтобы разделение содержало пустые строки, если оно не находит это значение разделения.

EX:

result[0] = "blabla"
result[1] = ""
result[2] = "blabla"

Таким образом, я знаю, что результат 1 был разделен на значение (INGREDIENTS:), и я могу присвоить его чему-то

Ответы [ 3 ]

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

Использование регулярных выражений может иметь проблемы с производительностью, если вы используете это в приложениях большого объема. Ниже приведено одно возможное регулярное выражение, которое вы могли бы использовать. Несколько сложно проанализировать линейку продуктов и строку «название компании», поскольку неясно, имел ли код продукта шаблон, а строка названия компании не имела «:», как и другие поля, поэтому регулярное выражение несколько "хакер" в этих областях:

using System;
using System.Text.RegularExpressions;
using System.Linq;

namespace so20190113_01 {
    class Program {
        static void Main(string[] args) {
            string text =
@"BFFPPB14 Dark Chocolate Dried Cherries 14 oz (397g)
INGREDIENTS: DARK CHOCOLATE (SUGAR, CHOCOLATE LIQUOR, COCOA BUTTER, ANHYDROUS MILK FAT, SOYA LECITHIN, VANILLIN [AN ARTIFICIAL FLAVOR]), DRIED TART CHERRIES (CHERRIES, SUGAR), GUM ARABIC, CONFECTIONER'S GLAZE.
CONTAINS: MILK, SOY
ALLERGEN INFORMATION: MAY CONTAIN TREE NUTS, PEANUTS, EGG AND WHEAT. 
01/11/2019
Description: Sweetened dried Montmorency cherries that are panned with dark chocolate. 
Storage Conditions: Store at ambient temperatures with a humidity less than 50%. Shelf Life: 9 months
Company Name
Item No.: 701804
Bulk: 415265
Supplier: Cherryland's Best
WARNING: CHERRIES MAY CONTAIN PITS";

            string pat =
                @"^\s*(?<product>\w+\s+\w+\s+\w*[^:]+)$
                |^ingredients:\s*(?<ingredients>.*)$
                |^contains:\s*(?<contains>.*)$
                |^allergen\s+information:\s*(?<allergen>.*)$
                |^(?<date>(\d{1,2}/\d{1,2}/\d{2,4}))$
                |^description:\s*(?<description>.*)$
                |^storage\sconditions:\s*(?<storage>.*)$
                |^shelf\slife:\s*(?<shelf>.*)$
                |^company\sname\s*(?<company>.*)$
                |^item\sno\.:\s*(?<item>.*)$
                |^bulk:\s*(?<bulk>.*)$
                |^supplier:\s*(?<supplier>.*)$
                |^warning:\s*(?<warning>.*)$
                ";

            Regex r = new Regex(pat, RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);

            // Match the regular expression pattern against a text string.
            Match m = r.Match(text); // you might want to use the overload that supports a timeout value
            Console.WriteLine("Start---");
            while (m.Success) {
                foreach (Group g in m.Groups.Where(x => x.Success)) {
                    switch (g.Name) {
                    case "product":
                        Console.WriteLine($"Product({g.Success}): '{g.Value.Trim()}'");
                        break;
                    case "ingredients":
                        Console.WriteLine($"Ingredients({g.Success}): '{g.Value.Trim()}'");
                    break;
                    // etc.
                    }
                }
                m = m.NextMatch();
                }

            Console.WriteLine("End---");
            }
        }
    }
0 голосов
/ 14 января 2019

Я думаю, что парсер - это единственный способ. Первоначально я пытался использовать это регулярное выражение:

^([\w \.]+?):([\s\S]+?)(?=((^[\w \.]+?):))

Ключевым компонентом здесь является прогноз ?=, который позволяет строке соответствовать всему тексту от метки к метке. Однако он не работает с конечной позицией, поскольку не предшествует другой метке, и я не смог найти регулярное выражение, которое прекращает сопоставление с шаблоном, который может не существовать. Если это регулярное выражение существует, вы можете сделать все это в одной строке кода:

KeyValuePair<string, string>[] kvs = null;

//one line of code if the look-ahead would also consider non-existent matches
kvs = Regex.Matches(text, @"^([\w \.]+?):([\s\S]+?)(?=((^[\w \.]+?):))", RegexOptions.Multiline)
    .Cast<Match>()
    .Select(x => new KeyValuePair<string, string>(x.Groups[1].Value, x.Groups[2].Value.Trim(' ', '\r', '\n', '\t')))
    .ToArray();

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

KeyValuePair<string, string>[] kvs = null;

//Otherwise, you have to write a parser
//get all start indexes of labels
var matches = Regex.Matches(text, @"^.+?:", RegexOptions.Multiline).Cast<Match>().ToArray();

kvs = new KeyValuePair<string, string>[matches.Length];

KeyValuePair<string, string> GetKeyValuePair(Match match1, int match1EndIndex)
{
    //get the label
    var label = text.Substring(match1.Index, match1.Value.Length - 1);

    //get the desc and trim white space
    var descStart = match1.Index + match1.Value.Length + 1;
    var desc = text
        .Substring(descStart, match1EndIndex - descStart)
        .Trim(' ', '\r', '\n', '\t');

    return new KeyValuePair<string, string>(label, desc);
}

for (int i = 0; i < matches.Length - 1; i++)
{
    kvs[i] = GetKeyValuePair(matches[i], matches[i + 1].Index);
}

kvs[kvs.Length - 1] = GetKeyValuePair(matches[matches.Length - 1], text.Length);

foreach (var kv in kvs)
{
    Console.WriteLine($"{kv.Key}: {kv.Value}");
}
0 голосов
/ 14 января 2019

Итак, если ваше требование:

  • найти строку, начинающуюся с указанного слова

используйте Linq

использовать StartsWith

код

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;

namespace ConsoleApp12
{
    class Program
    {
        public static void Main(string[] args)
        {
            // test string

            var str = @"BFFPPB10 Dark Chocolate Macadamia Nuts 11 oz (312g)\r\nINGREDIENTS: DARK CHOCOLATE (SUGAR, CHOCOLATE, COCOA BUTTER, \r\nANHYDROUS MILK FAT, SOY LECITHIN, VANILLA), MACADAMIA NUTS, SEA SALT.\r\nCONTAINS: MACADAMIA NUTS, MILK, SOY.\r\nALLERGEN INFORMATION: MAY CONTAIN OTHER TREE NUTS, PEANUTS, EGG AND\r\nWHEAT.\r\n01/11/2019\r\nDescription: Dry roasted, salted macadamias covered in dark chocolate.\r\nStorage Conditions: Store at ambient temperatures with a humidity less than 50%. \r\nShelf Life: 12 months\r\nBlain's Farm & Fleet\r\nItem No.: 701772\r\nBulk: 421172\r\nSupplier: Devon's\r\n";

            // Keys

            const string KEY_INGREDIENTS = "INGREDIENTS:";
            const string KEY_CONTAINS = "CONTAINS:";
            const string KEY_ALLERGEN_INFORMATION = "ALLERGEN INFORMATION:";
            const string KEY_DESCRPTION = "Description:";
            const string KEY_STORAGE_CONDITION = "Storage Conditions:";
            const string KEY_SHELFLIFE = "Shelf Life:";
            const string KEY_ITEM_NO = "Item No.:";
            const string KEY_BULK = "Bulk:";
            const string KEY_SUPPLIER = "Supplier:";
            const string KEY_WARNING = "WARNING:";
            const string KEY_YEAR_Regex = @"^\d{1,2}/\d{1,2}/\d{4}$";
            const string KEY_AFTER_COMPANY_NAME = KEY_ITEM_NO;


            // Helpers

            var keys = new string[]
            { KEY_INGREDIENTS, KEY_CONTAINS, KEY_ALLERGEN_INFORMATION, KEY_DESCRPTION, KEY_STORAGE_CONDITION,
                KEY_SHELFLIFE, KEY_ITEM_NO, KEY_BULK, KEY_SUPPLIER, KEY_WARNING };

            var lines = str.Split(new string[] { @"\r\n" }, StringSplitOptions.RemoveEmptyEntries);

            void log(string key, string val)
            {
                Console.WriteLine($"{key} =>  {val}");
                Console.WriteLine();
            }

            void removeLine(string line)
            {
                if (line != null) lines = lines.Where(w => w != line).ToArray();
            }


            // get Multi Line Item with key

            string getMultiLine(string key)
            {
                var line = lines
                            .Select((linetxt, index) => new { linetxt, index })
                                .Where(w => w.linetxt.StartsWith(key))
                                .FirstOrDefault();

                if (line == null) return string.Empty;

                var result = line.linetxt;

                for (int i = line.index + 1; i < lines.Length; i++)
                {
                    if (!keys.Any(a => lines[i].StartsWith(a)))
                        result += lines[i];
                    else
                        break;
                }

                return result;
            }


            // get single Line Item before spesic key if the Line is not a key

            string getLinebefore(string the_after_key)
            {
                var the_after_line = lines
                            .Select((linetxt, index) => new { linetxt, index })
                                .Where(w => w.linetxt.StartsWith(the_after_key))
                                .FirstOrDefault();

                if (the_after_line == null) return string.Empty;

                var the_before_line_text = lines[the_after_line.index - 1];

                //not a key
                if (!keys.Any(a => the_before_line_text.StartsWith(a)))
                    return the_before_line_text;
                else
                    return null;
            }



            // 1st get item without key

            var itemName = lines.FirstOrDefault();
            removeLine(itemName);

            var year = lines.Where(w => Regex.Match(w, KEY_YEAR_Regex).Success).FirstOrDefault();
            removeLine(year);

            var companyName = getLinebefore(KEY_AFTER_COMPANY_NAME);
            removeLine(companyName);

            //2nd get item with Keys

            var ingredients = getMultiLine(KEY_INGREDIENTS);
            var contanins = getMultiLine(KEY_CONTAINS);
            var allergenInfromation = getMultiLine(KEY_ALLERGEN_INFORMATION);
            var description = getMultiLine(KEY_DESCRPTION);
            var storageConditions = getMultiLine(KEY_STORAGE_CONDITION);
            var shelfLife = getMultiLine(KEY_SHELFLIFE);
            var itemNo = getMultiLine(KEY_ITEM_NO);
            var bulk = getMultiLine(KEY_BULK);
            var supplier = getMultiLine(KEY_SUPPLIER);
            var warning = getMultiLine(KEY_WARNING);


            // 3rd log

            log("ItemName", itemName);
            log("Ingredients", ingredients);
            log("contanins", contanins);
            log("Allergen Infromation", allergenInfromation);
            log("Year", year);
            log("Description", description);
            log("Storage Conditions", storageConditions);
            log("Shelf Life", shelfLife);
            log("CompanyName", companyName);
            log("Item No", itemNo);
            log("Bulk", bulk);
            log("Supplier", supplier);
            log("warning", warning);

            Console.ReadLine();
        }


    }
}

выдаст

ItemName => BFFPPB10 Орех макадамия темного шоколада 11 унций (312 г)

Ингредиенты => ИНГРЕДИЕНТЫ: ТЕМНЫЙ ШОКОЛАД (САХАР, ШОКОЛАД, КАКАО БАБОЧКА, безводный молочный жир, соевый лецитин, ваниль), орехи макадамия, Морская соль.

contanins => СОДЕРЖИТ: ОРЕХИ МАКАДАМИИ, МОЛОКО, СОЙ.

Информация об аллергене => АЛЛЕРГЕННАЯ ИНФОРМАЦИЯ: МОЖЕТ СОДЕРЖАТЬ ДРУГОЕ ДЕРЕВО Орехи, арахис, яйца и пшеница.

Год => 01/11/2019

Описание => Описание: макадамии сухие жареные, соленые темный шоколад.

Условия хранения => Условия хранения: Хранить при температуре окружающей среды температура с влажностью менее 50%.

Срок годности => Срок годности: 12 месяцев

CompanyName => Ферма и флот Блейна

Пункт № => Пункт №: 701772

Bulk => Bulk: 421172

Поставщик => Поставщик: Devon's

предупреждение =>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...