Низ към последователност от токени

Анализирам низове от командна последователност и трябва да конвертирам всеки низ в низ [], който ще съдържа командни токени в реда, в който са прочетени.

Причината е, че тези последователности се съхраняват в база данни, за да инструктират клиента на протокола да изпълни определена предписана последователност за отделни отдалечени приложения. В тези низове има специални токени, които трябва да добавя към низа [] сами по себе си, защото те не представляват данни, които се предават; вместо това те показват блокиращи паузи.

Последователностите не съдържат разделители. Може да има произволно количество специални жетони, намерени навсякъде в командна последователност, поради което не мога просто да анализирам низовете с регулярен израз. Освен това всички тези специални команди в последователността са обвити с ${}

Ето пример за данните, които трябва да анализирам в токени (P1 показва блокираща пауза за една секунда):

"some data to transmit${P1}more data here"

Полученият масив трябва да изглежда така:

{ "some data to transmit", "${P1}", "more data here" }

Мисля, че LINQ може да помогне с това, но не съм толкова сигурен. Единственото решение, което мога да измисля, е да преминавам през всеки знак, докато се намери $ и след това да открия дали е налична специална команда за пауза и след това да анализирам последователността от там с помощта на индекси.


person Jeff LaFay    schedule 24.08.2011    source източник
comment
Не разбирам защо не можете да използвате Regex.Split -- можете ли да обясните по-подробно?   -  person Gabe    schedule 24.08.2011
comment
Защо не използвате набор от разделители? Това би омаловажило този проблем.   -  person Tejs    schedule 24.08.2011
comment
Вие повече или по-малко точно описвате лексер. Трябва да има достатъчно информация за това как работят лексерите и те са сравнително лесни за изпълнение.   -  person Justin    schedule 24.08.2011
comment
@Gabe, защото нямам разделители.   -  person Jeff LaFay    schedule 24.08.2011
comment
@Tejs, защото командните низове са данни, споделени между няколко приложения и не могат да бъдат променени, за да отговарят на моето едно приложение, което пиша.   -  person Jeff LaFay    schedule 24.08.2011
comment
Съжалявам, все още не разбирам какво не е наред с регулярен израз. Можете ли да публикувате пример, при който Regex.Split(str, @"(\${.*?})") няма да работи?   -  person Gabe    schedule 24.08.2011
comment
@Gabe, защо не публикуваш отговор, ако имаш решение? Вашето използване на разделяне с вашия модел работи. Той добавя празни низове към масива.   -  person Jeff LaFay    schedule 24.08.2011


Отговори (3)


Една опция е да използвате Regex.Split(str, @"(\${.*?})") и да игнорирате празните низове, които получавате, когато имате два специални токена един до друг.

Може би Regex.Split(str, @"(\${.*?})").Where(s => s != "") е това, което искате.

person Gabe    schedule 24.08.2011

Добре, както беше споменато в коментарите, предлагам ви да прочетете за лексерите. Те имат силата да направят всичко и повече от описаното от вас.

Тъй като вашите изисквания са толкова прости, ще кажа, че не е твърде трудно да напишете лексера на ръка. Ето някакъв псевдокод, който може да го направи.

IEnumerable<string> tokenize(string str) {

    var result = new List<string>();
    int pos = -1;
    int state = 0;
    int temp = -1;

    while( ++pos < str.Length ) {
        switch(state) {
            case 0:
                if( str[pos] == "$" ) { state = 1; temp = pos; }
                break;
            case 1:
                if( str[pos] == "{" ) { state = 2; } else { state = 0; }
                break;
            case 2:
                if( str[pos] == "}" } {
                    state = 0;
                    result.Add( str.Substring(0, temp) );
                    result.Add( str.Substring(temp, pos) );
                    str = str.Substring(pos);
                    pos = -1;
                }
                break;
            }
    }

    if( str != "" ) {
        result.Add(str);
    }

    return result;
}

Или нещо такова. Обикновено бъркам параметрите на Substring при първия опит, но това е общата идея.

Можете да получите много по-мощен (и по-лесен за четене) лексер, като използвате нещо като ANTLR.

person riwalk    schedule 24.08.2011
comment
Може би ANTLR е моят отговор тогава, защото това е, което се опитвам да отида, нещо по-просто и по-лесно за четене/разбиране. Мога да премина през низа, за да токенизирам последователностите без проблем. - person Jeff LaFay; 24.08.2011
comment
@jlafay, донякъде зависи от това колко мощен трябва да бъде. Ако очаквате нещата да станат по-сложни, използвайте ANTLR. Ако алгоритъмът по-горе реши проблема ви напълно, тогава използвайте вместо него написан на ръка. Кодът по-горе е по-добре описан като държавна машина, отколкото като цикъл, и е много подобен на това, което ANTLR ще генерира, така че е малко вероятно да спечелите много по отношение на ефективността. Всичко зависи от вашите нужди. - person riwalk; 24.08.2011

Използвайки малко от предложението на Gabe, измислих решение, което прави точно това, което исках да направя:

string tokenPattern = @"(\${\w{1,4}})";
string cmdSequence = "${P}test${P}${P}test${P}${Cr}";

string[] tokenized = (from token in Regex.Split(cmdSequence, tokenPattern)
                      where token != string.Empty
                      select token).ToArray();

С командната последователност в горния пример, масивът съдържа това:

{ "${P}", "test", "${P}", "${P}", "test", "${P}", "${Cr}"}
person Jeff LaFay    schedule 24.08.2011