Страницы

Поиск по вопросам

Показаны сообщения с ярлыком синтаксический-анализ. Показать все сообщения
Показаны сообщения с ярлыком синтаксический-анализ. Показать все сообщения

среда, 15 апреля 2020 г.

Синтаксический анализатор (построение грамматики) ?

#синтаксический_анализ #парсер

                    
Нужно построить синтаксический анализатор. В частности для языка создать  контекстно-зависимую
грамматику( LL) Везде встречаются примеры только для чисел и знаков, у меня присутствуют
только 4 буквы(A, B, C, D)  и для них надо построить грамматику .
Есть ли примеры построения грамматики ?    


Ответы

Ответ 1



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

понедельник, 13 апреля 2020 г.

Парсинг XML

#xml #android #синтаксический_анализ

                    
Работаю на андроид с Xml. Столкнулась с тем, что мне нужно парсить xml такого вида:




Понимаю, как достать текст, атрибуты, а как мне достать слово "Пример"?    


Ответы

Ответ 1



Надо парсить SAX'ом, простейший пример использования здесь Касаемо секции CDATA ее надо ловить в хэндлере DefaultHandler.characters(), причем с конкатенацией, поскольку он может вызываться несколько раз. P.S. @atnartur неправ, поскольку парсер вернет без , вернет только Пример

Ответ 2



Простой пример Serializing with CDATA blocks http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php

четверг, 19 марта 2020 г.

Парсинг XML

#xml #php #синтаксический_анализ


Есть xml файл test.xml, в котором содержится:


1
20121126 03:57:10
test
Rxxx
WMIDxxx
0
3
95.28.75.19

null


null


-1



0



Вот как, средствами php, получить значение amount?    


Ответы

Ответ 1



$xml = new DomDocument; $xml -> ValidateOnParse = true; $xml -> load('test.xml'); $amount = $xml -> getElementsByTagName('amount')->item(0)->nodeValue;

Ответ 2



SimpleXML

Ответ 3



Вот здесь пример.

вторник, 17 марта 2020 г.

Распарсить json

#json #php #синтаксический_анализ


Добрый вечер.
Так как я не очень силен в php однако пишу сейчас vk-api приложение, хотелось бы
узнать следующее: как в PHP можно распарсить json? Знаю что через json_decode, но как
именно не знаю. Хотелось бы записать строчку id в переменную.
Сам JSON:
response: [{
id: 123456,
first_name: 'First',
last_name: 'Name'
}]
    


Ответы

Ответ 1



$responseJson = "[{ id: 123456, first_name: 'First', last_name: 'Name' }]"; // ваш пример строки $response = json_decode($responseJson, true); // преобразование строки в формате json в ассоциативный массив $id = $response[0]['id']; // ваш искомый id Документация: JSON json_encode() json_decode() работа с массивами

воскресенье, 15 марта 2020 г.

Парсинг html с помощью Jsoup

#android #jsoup #синтаксический_анализ


Есть html строка:

DSC_0320_1

Нужно получить адрес изображения:http://www.postfactum.ks.ua/wp-content/uploads/2015/06/DSC_0320_1-300x163.jpg. Пытаюсь парсить: Document doc = Jsoup.parse(image_url); if (doc.select("img[src]").size() > 0) { Element image = doc.select("img[src]").get(0); } else { image_url = "null"; } Ошибок нету, но строка пуста. В чем может быть проблема?


Ответы

Ответ 1



Во-первых вам нужно использовать метод first() или last() в зависимости от порядка елемента. В вашем случае нужно использовать метод first(). Element image = doc.select("img[src]").first();. Во-вторых вы не указали атрибут "src": image_url = image.attr("src");. Полный код: Document doc = Jsoup.parse(mage_url); if (doc.select("img[src]").size() > 0) { Element image = doc.select("img[src]").first(); image_url = image.attr("src"); } else { image_url = "null"; }

воскресенье, 26 января 2020 г.

Защита от парсинга сайта

#php #синтаксический_анализ


Заметил в логах всякую кривую хрень:
То идет куча запросов на не существующие файлы - типа проверяют что за движок или
пытаются ломануть
То еще всякая мутотень, могут программу написать которая сайт будет выкачивать (это
как бы в теории)
Как можно на php сделать защиту от парсинга сайта?
типа такую же как на яндекс марките?
В целях экономия трафика и мощности сервера
Ну уж очень дрючат ироды
И еще проблема - это поисковики - они тоже дрючат но в бан их никак нельзя    


Ответы

Ответ 1



Для поисковиков можно задать период обновления информации на сайте, может тогда меньше начнут? Насчёт парсинга - что за сайт, что так боитесь за его содержимое? Или что вы подразумеваете под парсингом? Периодически запускают выкачивание всего сайта с целью парсинга?

Ответ 2



Собственно я придумал крутую защиту от парсинга При входе на сайт запихиваем сессию с уникальным ключом Потом генерируем кодированный javascript который отправляет с помощью ajax при загрузке сайта этот самый уникальный ключ и устанавливает новую сессию nobot Короче говоря потом проверяем если допустим 10 заходов на сайт а сессии nobot нет то нафиг в бан Походу это крутая защита вот только фиг знает гугл и поисковики тоже попадут в бан

Ответ 3



С большой вероятностью это просто "хакеры". Боты, пытающиеся определить движок. Вообще никакая не редкость в наше время. Защитить содержимое от парзинга и при этом отдавать нормальное содержимое поисковикам невозможно. Про хитрые схемы с AJAX Вам уже написали - это хороший вариант, если замудрить с динамической подгрузкой содержимого, то парзить будет крайне сложно. Простой вариант сделать задержку загрузки страниц (настраивается, к примеру, в nginx, если он у Вас есть, несложным образом). Исходим из того, что обычный пользователь не может читать по 100 страниц за минуту. Если такое происходит - то это бот (поисковый, хакерский или парзер конкурентов). Если же огромное количество запросов на несуществующие URI (которые не являются ссылками на старую версию вашего сайта) идет с одного IP, то это "хакеры", детектирующие широко распространенные движки. Их можно просто банить.

Помощь в парсинге JSON с помощью Json.NET

#json #net #синтаксический_анализ


Имеется следующий Json:

{
"status":"ok",
"count":1,
"data":{
    **"1"**:{
        "members_count":100,
        "description":"Закрытый клан, в состав которого входят _лишь_ разработчики
игры "World of Tanks". Заявки, посланные командиру клана через форум, _НЕ РАССМАТРИВАЮТСЯ_ . ",
        "description_html":"

Закрытый клан, в состав которого входят _лишь_ разработчики игры "World of Tanks".

Заявки, посланные командиру клана через форум, _НЕ РАССМАТРИВАЮТСЯ_ .

", "created_at":1293024672, "request_availability":false, "updated_at":1381187704, "private":null, "abbreviation":"WG", "emblems":{ "large":"http://clans.worldoftanks.ru/media/clans/emblems/clans_1/1/emblem_64x64.png", "small":"http://clans.worldoftanks.ru/media/clans/emblems/clans_1/1/emblem_24x24.png", "bw_tank":"http://clans.worldoftanks.ru/media/clans/emblems/clans_1/1/emblem_64x64_tank.png", "medium":"http://clans.worldoftanks.ru/media/clans/emblems/clans_1/1/emblem_32x32.png" }, "clan_id":1, "members":{ **"196632"**:{ "account_id":196632, "created_at":1293126248, "updated_at":0, "account_name":"Wrobel", "role":"private", "role_i18n":{ "ru":"солдат", "fr":"Soldat", "en":"Soldier", "th":"ทหาร", "vi":"Quân nhân", "de":"Schütze", "tr":"Asker", "it":"солдат", "hu":"солдат", "zh-cn":"战士", "pl":"żołnierz", "ms":"солдат", "cs":"Voják", "es":"Soldado" } }, "18458":{ "account_id":18458, "created_at":1360836543, "updated_at":0, "account_name":"alienraven", "role":"diplomat", "role_i18n":{ "ru":"дипломат", "fr":"Diplomate", "en":"Diplomat", "th":"ทูต", "vi":"ngoại giao", "de":"Diplomat", "tr":"Diplomat", "it":"дипломат", "hu":"дипломат", "zh-cn":"外交官", "pl":"dyplomata", "ms":"дипломат", "cs":"Diplomat", "es":"Diplomático" } }, Возникла проблема с описанием класса для newtown json т.к. имеются ключи("1","196632"), которые имеют нестатические названия ("1","196632"). Их названия меняются в зависимости от того к какому id я обращаюсь, через API. [JsonObject(MemberSerialization.OptIn)] public class Clan { [JsonProperty("status")] public string Status { get; set; } [JsonProperty("count")] public int Count { get; set; } public Data Data { get; set; } } [JsonObject(MemberSerialization.OptIn)] public class Data { //[JsonProperty("что писать вместо 1")] //public что писать вместо 1 { get; set; } }


Ответы

Ответ 1



Такие json-ы можно десериализовать в Dictionary. Вот сильно упрощенный пример: public class TestClass { public const string Json = @"{""Name"":""Foo"",Bs:{""100"":{""Value"":1,""Flag"":false},""500"":{""Value"":2,""Flag"":true}}}"; [Test] public void Test() { var a = JsonConvert.DeserializeObject(Json); } } public class A { public string Name { get; set; } public Dictionary Bs { get; set; } } public class B { public int Value { get; set; } public bool Flag { get; set; } }

четверг, 9 января 2020 г.

Алгоритм выделения блока кода из позиции курсора?

#алгоритм #синтаксический_анализ


Допустим, есть код следующий
| - каретка  

function a () {
 let b = 20;
 |
}


В таком случае легко написать алгоритм выделения блока { let b = 20 } просто пройти
сначала направо или налево, найти положение открывающей/закрывающей скобки, сделать
counter равный 1 и потом пойти в обратную сторону прибавляя или уменьшая счетчик в
зависимости от того, какая скобка попадается в очередной раз и когда он будет 0 означает
что блок закрылся.  

Это так же сработает и для { | {} {} {} }

Но это не будет работать для { {} | {} }, как быть в этом случае? Есть ли вообще
решение без разбиения всего кода на токены, выделение блоков, ast и прочее? Ну и как
вообще лучше данную задачу решить?
    


Ответы

Ответ 1



Ну почему же не сработает? Открывающая скобка - +1, закрывающая - -1. Идем в вашем случае влево, получается последовательность (пишу значение после обработки каждой скобки по пути) -1, 0, +1. Все, стоп! +1 - найдена искомая открывающая скобка. Идем вправо, имея +1. Далее - +2, +1, +2, +1, 0. Стоп! 0 - найдена закрывающая скобка. Не сработает при наличии скобок в комментариях или строковых литералах, например. Тут нужно дополнительно разбираться...

вторник, 31 декабря 2019 г.

Парсинг строки при помощи регулярных выражений. Задачка на кавычки и пробелы.

#c_sharp #регулярные_выражения #синтаксический_анализ #powershell


Всю голову сломал себе =) из текстового файла читаются строки. В одной строке может
быть "несколько параметров". Нужно строчку разделить на эти параметры.
Есть несколько условий:


Параметры разделяются пробелами.


Параметр может содержать пробелы, но в это случае он обязательно должен быть заключен
в двойные кавычки ("").


Никто не запрещает заключать параметры без пробелов в кавычки.


Например нужно следующую строку разбить на три.
D:\test "D:\test\Название папки" "D:\test\test"

На выходе из функции должен получить 3 строки содержащие соответственно:
D:\test
D:\test\Название папки
D:\test\test

Подскажите, как бы мне это сделать? Использование string.split() не подходит в этом
случае, потому решил, что это нужно реализовывать при помощи regexp.match()...
Задача еще усложняется тем, что количество параметров может быть не ограничено. Под
концом строки подразумевается \r\n
Очередной раз благодарю Вас за помощь в моей задаче    


Ответы

Ответ 1



Попробуйте ".+?"|[^ ]+ Только надо будет удалять кавычки у параметров, у которых они есть.

Ответ 2



Вот вам ручной парсер (стащил отсюда): public static string[] SplitArguments(string commandLine) { var parmChars = commandLine.ToCharArray(); var inSingleQuote = false; var inDoubleQuote = false; for (var index = 0; index < parmChars.Length; index++) { if (parmChars[index] == '"' && !inSingleQuote) { inDoubleQuote = !inDoubleQuote; parmChars[index] = '\n'; } if (parmChars[index] == '\'' && !inDoubleQuote) { inSingleQuote = !inSingleQuote; parmChars[index] = '\n'; } if (!inSingleQuote && !inDoubleQuote && parmChars[index] == ' ') parmChars[index] = '\n'; } return (new string(parmChars)).Split(new[] {'\n'}, StringSplitOptions.RemoveEmptyEntries); } Поддерживает также и одинарные кавычки. Можно переписать в виде генератора, будет немного поэффективнее.

воскресенье, 29 декабря 2019 г.

Теория синтаксического анализатора

#синтаксический_анализ #лексический_анализатор


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

Следующим шагом должно быть разбиение лексем на классы (мне нужно отличить идентификаторы
в паскале).

Как отличить идентификаторы от не идентификаторов?
Выполнять проверку на на принадлежность к определенной множеству зарезервированных
слов (операторов и т д)?

При разбиении на классы нужно использовать дерево. Как я не понимаю.
На первом уровне должны быть типы лексем?
Каждым элементом кроны должен быть определенная лексема, которая встречается в коде?

Помогите, пожалуйста, разобраться.
    


Ответы

Ответ 1



Классификация лексем зависит, конечно, от языка. Для начала, идентификаторы паскаля начинаются с буквы, поэтому вы их никак не спутаете с операторами. Конкретно для паскаля есть фиксированный набор идентификаторов, которые опознаются как ключевые слова. Так что получив идентификатор, просто проверяйте, нет ли его в списке. Рискну предположить, что под "деревом" для классификации лексем подразумевается следующая конструкция: Лексема -- это либо идентификатор (начинается с буквы и содержит буквы, цифры и знак подчёркивания), либо скобка, либо оператор, либо знак пунктуации. Идентификатор -- это либо ключевое слово (список прилагается), либо имя. Скобка -- это либо круглая скобка, либо квадратная (фигурные выбросил препроцессор), может быть открывающей и закрывающей. Оператор -- это один из списка (плюс, минус, ...) И так далее. Видите дерево?

Ответ 2



Самый простой способ получить пару лексический/синтаксический анализатор на практике сгенерировать его автогенератором из БНФ грамматики языка с помошью Flex и Bison (lex и yacc). Если хотите написать самому нормальный парсер курите теорию конечных автоматов и формальных языков. Если пытаться интутивно писать его, то с вероятностью 99% после первой попытки получится медленная, плохо-читаемая конструкция состоящая из условных переходов и сравнений строк чуть менее чем полностью. При этом сама эта конструкция на самом деле будет являться кривой реализацией конечного автомата для данной грамматики, но написавший её может даже и не подозревать что это так. Вот пример уже готовой lex и yacc грамматики для Паскаля. Всего 1000 строк кода. scanner.l; parser.y; P.S. Если захотите скомпилировать эту грамматику под Windows стоит брать flex.exe bison.exe из Cygwin или MinGW, а не с сайта GNU там почему-то лежат старые версии.

суббота, 28 декабря 2019 г.

Json-парсер для Delphi

#json #delphi #синтаксический_анализ


Чем парсить Json? Есть какие нибудь нормальные и легкие в освоении парсеры Json?    


Ответы

Ответ 1



Все ведь на официальном сайте JSONа перечислены: JSON delphi library Delphi Web Utils json-superobject tiny-json

Ответ 2



uses Data.DBXJSON //.. var JSONObject: TJSONObject; begin JSONObject := TJSONObject.ParseJSONValue('{"name","value"}') as TJSONObject; ShowMessage(JSONObject.Get('name').JsonValue.Value) но не хочет парсить данные типа {123:"value"} написал такой "патч": class function TJSONObject.ParsePair(const Br: TJSONByteReader; const Parent: TJSONObject): Integer; var Pair: TJSONPair; CommaPos: Integer; begin Pair := TJSONPair.Create; Parent.AddDescendant(Pair); // CommaPos := ParseString(Br, Pair); ///////////////////////////////////////// { Заміна щоб бачило рядки типу {33:33} ConsumeWhitespaces(Br); if Br.PeekByte in [Ord('0')..Ord('9')] then CommaPos := ParseNumber(Br, Pair) else CommaPos := ParseString(Br, Pair); {/} if CommaPos > 0 then begin ConsumeWhitespaces(Br); if Br.Empty then Exit(-Br.Offset); if Br.PeekByte <> Ord(':') then Exit(-Br.Offset); Br.ConsumeByte; ConsumeWhitespaces(Br); CommaPos := ParseValue(Br, Pair); end; Result := CommaPos; end; Просто замени соответствующую функу (можешь сохранить в другом файле)

Ответ 3



Начиная с Delphi 2010 присутствует встроенная поддержка json (модуль Data.DBXJSON) в рамках технологии dbExpress, также в дополнение к уже перечисленным библиотекам, появилась еще одна от Andreas Hausladen, автора IDE Tools для Delphi: Json Data Objects, поддерживаются версии начиная с 2009.

Ответ 4



Результата стоило бы стыдиться, но меня не устроил ни один из имевшихся вариантов, поэтому я написал свой: https://github.com/badunius/myJSON UPD: Не устроила прежде всего громоздкость, требующая для работы со вложенностью либо заводить орду переменных, либо выстраивать чересчур нечитаемые цепочки. Приведение типов хоть и прогрессивно, но тоже читабельности не добавляет. Я привык работать с JSON в JS, поэтому мне была важна максимальная сходность синтаксиса. Ах да, и главным требованием, конечно, была совместимость с Delphi 7, потому что я всё ещё ею пользуюсь.

Ответ 5



В последних версиях Delphi, есть встроенная библиотека JSON

понедельник, 23 декабря 2019 г.

Разработка синтаксического анализатора языка C

#cpp #синтаксический_анализ


На лабе задали написать синтаксический анализатор языка С:


  Написать синтаксический анализатор, обнаруживающий наибольшее число ошибок, для
приведённой ниже грамматики (данная грамматика является упрощённым вариантом грамматики
языка С):


< program >: < type >   ’main’   ‘(‘   ‘)’   ‘{‘   < statement >   ‘}’
< type >: ‘int’
 | ‘bool’
 | ‘void’
: 
  | < declaration > ‘;’
  | ‘{‘ < statement > ‘}’
  | < for >   < statement >
  | < if >      < statement >
  | < return >
< declaration >: < type >   < identifier >   < assign >
< identifier >: < character >< id_end >
< character >: ‘a’ | ‘b’ | ‘c’ | ‘d’ | ‘e’ | ‘f’ | ‘g’ | ‘h’ | ‘i' | ‘j’ | ‘k’ |
‘l’ | ‘m’ | ‘n’ | ‘o’ | ‘p’ | ‘q’ | ‘r’ | ‘s’ | ‘t’ | ‘u’ | ‘v’ | ‘w’ | ‘x’ | ‘y’ |
‘z’ | ‘A’ | ‘B’ | ‘C’ | ‘D’ | ‘E’ | ‘F’ | ‘G’ | ‘H’ | ‘I’ | ‘J’ | ‘K’ | ‘L’ | ‘M’ |
‘N’ | ‘O’ | ‘P’ | ‘Q’ | ‘R’ | ‘S’ | ‘T’ | ‘U’ | ‘V’ | ‘W’ | ‘X’ | ‘Y’ | ‘Z’ | ‘_’
  :
| 
:
  | ‘=’ 
: 
  | 
: 
: ‘0’ | ‘1’ | ‘2’ | ‘3’ | ‘4’ |  ‘5’ | ‘6’ | ‘7’ | ‘8’ | ‘9’
:
  | 
: ‘for’ ‘(‘  ‘;’  ‘;’ ‘)’
:        
  |           
:  ‘<’ | ‘>’ | ‘==’ | ‘!=’
: ‘if’ ‘(‘  ‘)’
: ‘return’  ‘;’


Первые 4-е строки вроде понял(это описание мейна). А что идет дальше? Какие варианты
кода может реализовать данный шаблон? Может у кого уже есть какие-то наработки на языке
С++? В сети ничего не нашел(т.к. не знаю что конкретно мне нужно, тупо как-то проанализировать
код?). В любом случае пример кода не помешал бы.
    


Ответы

Ответ 1



@Alerr, исходники yacc или bison Вам сейчас совершенно не помогут. А вот Вирта почитайте. Еще можете поискать исходники "калькуляторов". Они довольно распространены и обычно там для трансляции выражения, например, в обратную польскую нотацию применяют как раз алгоритм рекурсивного спуска (он тут тоже подойдет). Думаю, рекурсивный разбор как раз будет в следующей лабе. В принципе ничего сложного (здесь Вам уже сказали об этом) тут нет. Для начала напишите лексический анализатор. Это функция, которая читает ввод и возвращает лексему - структуру, в которой описано что мы прочли идентификатор, ключевое слово, число, знак операции, скобку и т.п. При этом часть грамматики у Вас уйдет в этот лексический анализатор и грамматика упростится. Далее, просто пишете набор функций в соответствии с грамматикой. Ну, пожалуй надо придумать, как сообщать об ошибках, принять решение, заканчивать разбор при первой же ошибке или пробовать "восстановиться" и разбирать дальше (это сложнее) и т.п. Я начну какой-то сильно псевдокод, думаю станет понятней int programm () { int rc; lexem_t lx = get_lexem(); if (lx.type == KEYWORD && (lx.subtype == INT || lx.subtype == VOID || lx.subtype == BOOL)) { lx = get_lexem(); if (lx.type == IDENT && strcmp(lx.value, "main") == 0) { // очевидно, в нормальном Си тут нужно вызывать разбор списка параметров // но в нашей грамматике должна идти пара '(' ')' if ((rc = parentheses()) == OK) { lx = get_lexem(); if (lx.type == SPEC && lx.char_value == '{') if ((rc = statement()) == OK) { // уффф! почти добрались до конца lx = get_lexem(); if (lx.type == SPEC && lx.char_value == '}') return OK; // Ура!!! rc = error_message(lx, ...); // ждали '}', а прочли ??? } return rc; } rc = error_message(lx, ...); // ждали '{', а прочли ??? } return rc; // далее в том же духе обработка ошибок, просто в этом "редакторе" мало строк помещается, набирать с indent трудно ... } // конец if (lx.type == KEYWORD ... return error_message(lx, ....); // ждали int | void | bool, а прочли ??? } Надеюсь, в общих чертах понятно. Еще одно, Вам почти наверняка понадобится функция аналогичная ungetc(c), но для лексемы. Не забудьте учесть, продумывая структуры данных.

Ответ 2



Здесь простая грамматика языка, которую вполне можно реализовать без использования lex и yacc. Для лексического анализа нужен лексический анализатор, который по набору символов будет определять, какая именно лексема встретилась. Синтаксический анализатор, опираясь на поток лексем, может проверять синтаксис. Лексический анализ В данном конкретном случае достаточно рассматривать один символ из входного потока, для того, чтобы понять, о какой лексеме идёт речь. Такой алгоритм называется анализом с предпросмотром на 1 символ. Обычно лексический анализатор пропускает все разделители (пробелы и символы новой строки), которые встречает во входном потоке, и не возвращает их в виде лексем. Для начала определим, какие лексемы перечислены в грамматике. Это ключевые слова: main, int, bool, void, for, if, return разделители и операторы {} () ; = < > == != а также идентификаторы и числа. Для упрощения кода мы добавим также лексему, которая будет соответствовать концу файла. Кроме того, если мы не можем распознать лексему, мы также должны вернуть специальный код. Итого: #define MAIN 1 #define INT 2 #define BOOL 3 #define VOID 4 #define FOR 5 #define IF 6 #define RETURN 7 #define LBRACE 101 /* { */ #define RBRACE 102 /* } */ #define LPAREN 103 /* ( */ #define RPAREN 104 /* ) */ #define SEMICOLON 105 /* ; */ #define ASSIGN 106 /* = */ #define LT 107 /* < */ #define GT 108 /* > */ #define EQUAL 109 /* == */ #define NOT_EQUAL 110 /* != */ #define IDENTIFIER 201 #define NUMBER 202 #define END 301 /* конец файла */ #define ERROR 302 /* ошибка */ Что такое лексический анализатор? В простейшем случае это одна функция, которая получает на вход поток символов, читает оттуда лексему и возвращает её. Для лексем IDENTIFIER и NUMBER нам потребуются также прочитанные символы, поэтому мы должны передавать адрес буфера для сохранения символов из входящего потока. Для того, чтобы прочитать несколько лексем, нужно вызвать функцию несколько раз. То есть сигнатура функции должна быть такой: int get_lexem(FILE* stream, char* buffer); Как её реализовать? Рассмотрим входной поток из символов "a=534", для упрощения пусть она будет без пробелов. Прочитав из потока символ '=' мы не можем остановиться, потому что в потоке может находиться лексема "==". Значит, мы читаем следующий символ, им оказывается '5' и теперь мы точно значем, что прочитали лексему ASSIGN а не EQUAL. Однако, символ '5' уже прочитан из потока, и при следующем вызове мы прочитаем оттуда уже '3' и '4'. Нам совершенно точно нужно сохранять между вызовами get_lexem последний прочитанный символ. Это можно делать разными способами, мы будем возвращать символ обратно в поток, вызывая стандартную функцию C ungetc. Такая ситуация будет возникать не каждый раз, а только тогда, когда лексема содержит больше одного символа. int get_lexem(FILE* stream, char* buffer, size_t buffer_length) { int ch = getc(stream); size_t i; /* пропускаем все пробелы, табуляции и символы новой строки */ while(isspace(ch)) ch = getc(stream); /* конец файла */ if(ch == EOF) return END; /* простые односимвольные лексемы */ if(ch == '{') return LBRACE; if(ch == '}') return RBRACE; if(ch == '(') return LPAREN; if(ch == ')') return RPAREN; if(ch == ';') return SEMICOLON; if(ch == '<') return LT; if(ch == '>') return GT; /* сложный случаай 1: = или == */ if(ch == '=') { ch = getc(stream); if(ch == '=') return EQUAL; ungetc(ch, stream); return ASSIGN; } /* сложный случай 2: != */ if(ch == '!') { ch = getc(stream); if(ch == '=') return NON_EQUAL; /* по грамматике сразу после ! обязан идти символ = */ /* если это не так, в лексеме ошибка */ return ERROR; } /* сложный случай 3: идентификатор или ключевое слово */ if(isalpha(ch) || ch == '_') { i = 0; do { buffer[i++] = ch; } while((ch = getc(stream)) != EOF && (isalpha(ch) || isdigit(ch) || ch == '_')); if(ch != EOF) ungetc(ch, stream); buffer[i] = '\0'; if(strcmp("main", buffer) == 0) return MAIN; if(strcmp("int", buffer) == 0) return INT; if(strcmp("bool", buffer) == 0) return BOOL; if(strcmp("void", buffer) == 0) return VOID; if(strcmp("for", buffer) == 0) return FOR; if(strcmp("if", buffer) == 0) return IF; if(strcmp("return", buffer) == 0) return RETURN; return IDENTIFIER; } /* сложный случай 4: число */ if(isdigit(ch)) { i = 0; do { buffer[i++] = ch; } while((ch = getc(stream)) != EOF && isdigit(ch)); if(ch != EOF) ungetc(ch, stream); buffer[i] = '\0'; return IDENTIFIER; } } Обратие внимание, как собираются символы лексем IDENTIFIER и NUMBER. Логика тут сложная, но каждый отдельный момент мы уже обсуждали. Если в потоке, например, находятся символы "_abc123=", то функция прочитает их все, включая '=', при этом символы "_abc123" попадут в буфер, а '=' мы вернём назад в поток. Конец файла возвращать в поток не нужно. Поскольку ключевые слова невозможно отличить от идентификаторов по правилам грамматики, а можно только по словарю, мы опеределяем их там же, где и идентификаторы. В приведённом коде есть две проблемы. Первая из них связана с тем, что возможно переполнение буфера. Скажем, мы сделаем размер буфера равным 1000 символов, а безумный программист придумает идентификатор длиной 1002 символа. Код и так получился довольно большим, поэтому я не стал писать обработку этой ошибки. Вторая проблема быстродействия, поскольку при чтении каждого идентификатора мы прогоняем его по списку ключевых слов самым медленным способом. В реальных компилятора здесь используют хеш-таблицы. Синтаксический анализ Простейший ручной способ синтаксического анализа — алгоритм рекурсивного спуска, который позволяет разбирать грамматики LL(1). Ваша грамматика как раз такая, что упрощает нам задачу. Правила кодируются очень простым способом. Например, что такое программа? program :: = type "main" "(" ")" "{" statement "}" Вот как это кодируется в C: char buffer[1024]; int program(FILE* stream) { int statement_result; int lexem = get_lexem(stream, buffer); if(lexem != INT || lexem != BOOL && lexem != VOID) return UNRECOGNIZED_MAIN_TYPE; lexem = get_lexem(stream, buffer); if(lexem != MAIN) return EXPECTED_MAIN; lexem = get_lexem(stream, buffer); if(lexem != LPAREN) return EXPECTED_LPAREN; lexem = get_lexem(stream, buffer); if(lexem != RPAREN) return EXPECTED_RPAREN; lexem = get_lexem(stream, buffer); if(lexem != LBRACE) return EXPECTED_LBRACE; statement_result = statement(stream); if(statement_result != OK) return statement_result; lexem = get_lexem(stream, buffer); if(lexem != RBRACE) return EXPECTED_RBRACE; return OK; } Многословно, но в принципе понятно. Константы OK, UNRECOGNIZED_MAIN_TYPE, EXPECTED_MAIN необходимо определить самостоятельно. Они описывают ошибки, которые грамматика может распознать. UNRECOGNIZED_MAIN_TYPE означает, что анализатор ожидает один из трёх типов, но получил что-то другое. Константа OK означает, что ошибок нет. Конструкция lexem = get_lexem(stream, buffer); if(lexem != RBRACE) return EXPECTED_RBRACE; встречается очень часто. Её можно назвать "требовать наличия лексемы во входном потоке". Для неё удобно завести отдельные функции: bool require_lexem1(FILE* stream, int expected1) { int lexem = get_lexem(stream); return lexem == expected1; } bool require_lexem2(FILE* stream, int expected1, int expected2) { int lexem = get_lexem(stream); return lexem == expected1 || lexem == expected2; } bool require_lexem3(FILE* stream, int expected1, int expected2, int expected3) { int lexem = get_lexem(stream); return lexem == expected1 || lexem == expected2 || lexem == expected3; } Теперь функцию program можно сделать проще и короче: int program(FILE* stream) { int statement_result; if(!require_lexem3(stream, INT, BOOL, VOID)) return UNRECOGNIZED_MAIN_TYPE; if(!require_lexem1(stream, MAIN)) return EXPECTED_MAIN; if(!require_lexem1(stream, LPAREN)) return EXPECTED_LPAREN; if(!require_lexem1(stream, RPAREN)) return EXPECTED_RPAREN; if(!require_lexem1(stream, LBRACE)) return EXPECTED_LBRACE; statement_result = statement(stream); if(statement_result != OK) return statement_result; if(!require_lexem1(stream, RBRACE)) return EXPECTED_RBRACE; return OK; } Функция statement сложнее и интереснее. Она на основании следующей лексемы принимает решение о том, какое из альтернативных правил применить. int statement(FILE* stream) { int statement_result; int lexem = get_lexem(stream); if(lexem == INT || lexem == BOOL || lexem == VOID) return declaration(stream); if(lexem == LBRACE) { statement_result = statement(stream); if(statement_result != OK) return statement_result; if(!require_lexem1(stream, RBRACE)) return EXPECTED_RBRACE; return OK; } if(lexem == FOR) return for_rule(stream); if(lexem == IF) return if_rule(stream); if(lexem == RETURN) return return_rule(stream); return UNRECOGNIZED_STATEMENT; } Эта функция рекурсивна, поэтому позволяет распознавать сложные конструкции вида { { { int i = 300; } } }. Для примера реализуем также функцию declaration: int declaration(FILE* stream) { if(!require_lexem1(stream, IDENTIFIER)) return EXPECTED_IDENTIFIER; if(!require_lexem1(stream, ASSIGN)) return EXPECTED_ASSIGN; if(!require_lexem2(stream, IDENTIFIER, NUMBER)) return EXPECTED_IDENTIFIER_OR_NUMBER; if(!require_lexem1(stream, SEMICOLON)) return EXPECTED_SEMICOLON; return OK; } Стараемся придерживаться принципа: для каждого правила в грамматике писать отдельную функцию, так удобнее проводить соответствия. В приведённом коде я отошёл от этого принципа, чтобы сделать код чуть короче, в частности, правила assign и assign_end попали внутрь declaration. В результате алгоритм спускается от верхнего общего правила к нижним (от program к declaration через statement), при этом некоторые функции могут вызывать друг друга рекурсивно. Именно поэтому алгоритм называется рекурсивным спуском. Реализовав все правила, мы получим полноценный анализатор программ, который сможет сообщать нам об ошибках. Напишем предпоследнюю функцию, которая превратит коды ошибок в текстовые сообщения: const char* get_message(int code) { switch(code) { case OK: return "Ok"; case UNRECOGNIZED_MAIN_TYPE: return "Unrecognized main type"; . . . } return "Unrecognized code"; } Главная функция программы оказывается очень простой: void main() { int code = program(stdin); const char* message = get_message(code); printf("%s\n", message); } Заключение Мы реализовали лексический анализатор (get_lexem), набор вспомогательных функций для проверки наличия лексем (require_lexem1, require_lexem2, require_lexem3) и набор функций для каждого правила грамматики (program, statement, declaration и множество других, которые вам предстоит написать самостоятельно). Функция get_message позволила нам выводить понятный текст вместо непонятных кодов. У нас получилось два набора констант: коды лексем (LPAREN, IDENTIFIER и т.д.) и коды ошибок (OK, EXPECTED_ASSIGN и т.д.) Вся программа целиком способна проверить код другой программы и вывести OK, если она соответствует грамматике. В противном случае она выводит сообщение об ошибке.

воскресенье, 22 декабря 2019 г.

Что лучше использовать для парсинга XAML?

#c_sharp #net #xaml #синтаксический_анализ


Недавно прочел, что для парсинга html не рекомендуют использовать регулярные выражения,
так как есть специальная библиотека. Ну и вот вопрос: есть ли что-то лучше регулярных
выражений для парсинга XAML?
Дополнительная информация:


Пишу на языке C#
Желательно чтобы инструмент работал со строкой, а не с файлом.
Цель: поиск повторяющейся разметки, сокращение ее объема и перестановка атрибутов. 

    


Ответы

Ответ 1



Если вам нужно детальное управление синтаксисом, то используйте парсеры XML: новый System.Xml.Linq.XDocument, старый System.Xml.XmlDocument. XAML — надмножество XML вместе со всеми пронстранствами и имён и прочим, поэтому любой парсер XML справится с XAML. Учтите, что в этом случае вы получите markup extension в виде простой строчки, а маппингом типов придётся заниматься вручную, если он вам понадобится. Если вам нужно глубокое понимание структуры (вместе с markup extension, маппингом на реальные типы и прочими надстройками XAML над базовым XML), то используйте парсер XAML: System.Xaml.XamlServices. Вы потеряете контроль над синтаксисом (если вы сериализуете не исключительно ваши классы), но у вас будет полностью распарсенное в объекты дерево, которое можно модифицировать и впоследствии сериализовать. Если вам нужна кросс-платформенность, то учтите, что парсер XAML в Mono не поддерживает все возможности (читать: ущербен до невозможности и абсолютно бесполезен), а на разных виндовых платформах свои отдельные парсеры диалектов XAML (читать: парсить можно только XAML родом из родной платформы). При этом в CoreCLR парсер XAML тоже, вроде, не намечается.

четверг, 12 декабря 2019 г.

Json как парсить на Java?

#java #json #синтаксический_анализ #json_encode


Здравствуйте!
Есть вот такой ответ от сервера: 

 {
    "p_result": "ok",
    "p_item": [
        {
            "p_id": 132,
            "p_name": "Николай"
        }, 
        {
            "p_id": 133,
            "p_name": "Светлана"
        }
    ]
 }


Если правильно понимаю, то это массив.
Нужно написать метод который будет по параметру находить нужный элемент
что то наподобие : 

_http.getArrayParamValue("p_name");


Никак не могу сообразить.

p.s. думаю для меня было бы полезнее некое указание, нежели ссылка на очерендую библиотеку.
Спасибо!

Решение. Ошибку тоже понял. @Josfey Спасибо огромное. Вы очень мне помогли! 

String str = null; 
String input = "данные полученные от сервера"; 

JsonParser parser = new JsonParser(); 
JsonObject mainObject = parser.parse(input).getAsJsonObject();
JsonArray pItem = mainObject.getAsJsonArray("p_item"); 

for (JsonElement user : pItem) {

    JsonObject userObject = user.getAsJsonObject(); 
    userObject.get("p_id"); 
    str = userObject.get("p_id").toString(); 
}

    


Ответы

Ответ 1



Не совсем понял, что именно вы хотите извлечь из приведённой json-структуры, но вот, например, как извлечь из неё имя пользователя c id = 132 с помощью GSON. String input = "тут ваша json-структура"; JsonParser parser = new JsonParser(); JsonObject mainObject = parser.parse().getAsJsonObject(); JsonArray pItem = mainObject.getAsJsonArray("p_item"); for (JsonElement user : pItem) { JsonObject userObject = user.getAsJsonObject(); if (userObject.get("p_id").getAsInt() == 132) { System.out.println(userObject.get("p_name")); return; } }

Ответ 2



На мой взгляд, самая лучшая на данный момент библиотека для работы с JSON - это Jackson от fasterxml. Измерял скорость сериализации/десериализации с помощью JMH, и эта библиотека показала наилучшие результаты на довольно сложной структуре данных. Кроме того, она имеет довольно богатый набор настроек: интернирование ключей, реакция на ошибки синтаксиса и т.п. Если вы хотите не просто сериализовывать/десериализовывать JSON-документы, а хотите менять структуру JSON-документа на лету, то рекомендую GSON.

Ответ 3



Рекомендую обратить внимание на json-simple Вот тут пример использования

Как вставить значение переменной внутрь строки?

#php #ооп #синтаксический_анализ #классы


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

Со вставкой возникла проблема: появляется ошибка следующего характера:


  Parse error: syntax error, unexpected '$rowNews' (T_VARIABLE) in C:\xampp\htdocs\sait12.loc\controller.php
on line 24


Я так понял, что это просто синтаксическая ошибка, что-то с кавычками, наверное.
Пробовал и в двойных, и без них вообще — не помогает.

Подскажите, в чём может быть дело, или научите как найти, что не так.

Код класса:

class DB_connect {
    protected $host = 'localhost';
    protected $baza = 'moya2';
    protected $user = 'admin123';
    protected $pass = '123';

        protected function __construct() {
            $connect = mysql_connect($this->host, $this->user, $this->pass);
            mysql_query("SET NAMES 'utf8';"); 
            mysql_query("SET CHARACTER SET 'utf8';"); 
            mysql_query("SET SESSION collation_connection = 'utf8_general_ci';");
            mysql_select_db($this->baza);
        }

        public function getArticles() {
            self::__construct();
            $row_articles = mysql_query('SELECT * FROM articles');
            return $row_articles;
        }

        public function insert($rowNews) {
            self::__construct();
            $addNews = "INSERT INTO articles (title, title_en, text, author, hashteg)
VALUES('"$rowNews['title']"', '"$rowNews['title_en']"', '"$rowNews['text']"', '"$rowNews['author']"',
'"$rowNews['hashteg']"')";
            $result = mysql_query($addNews) or die(mysql_error());
            mysql_close(self::__construct()->$connect);
        }
}


Доработал немного код...попытался добавить некую фильтрацию данных на входе и воспользовался
конкатенацией. Народ если у кого будет время и желание гляньте код. В правильном ли
направлении я вообще двигаюсь? Есть конечно очень сильное ощущение что операции с базой
типа выбрать, взять, вставить, обновить, удалить должны быть в отдельном классе. Так
ли это?)) заранее спасибо за ответы...

class DB_connect {

    /*Задаю свойства класса*/

    protected $host = 'localhost';
    protected $baza = 'moya2';
    protected $user = 'admin123';
    protected $pass = '123';
    private $connect = null; /*Правильно ли так задавать свойство для соединения
с базой?*/

    /*Конструктор для подключения к базе данных*/

        public function __construct() {
            $this->connect = mysql_connect($this->host, $this->user, $this->pass);
            mysql_query("SET NAMES 'utf8';"); 
            mysql_query("SET CHARACTER SET 'utf8';"); 
            mysql_query("SET SESSION collation_connection = 'utf8_general_ci';");
            mysql_select_db($this->baza);
        }

        /*Метод закрытия соединения с базой данных*/

        public function Close() {
            if($this->connect) {
                mysql_close($this->connect);
                echo 'Соединение с базой данных закрыто';
            }
        }

        /*Метод получения данных из таблицы articles*/

        public function getArticles() {
            self::__construct();
            $row_articles = mysql_query('SELECT * FROM articles');
            return $row_articles;
            self::Close();
        }

        /*Метод вставки статьи в таблицу articles*/

        public function insert($rowNews) {
            $rowNews = filter_var_array($rowNews);/*Попытка фильтрации входных данных*/
            self::__construct();
            $addNews = "INSERT INTO articles (title, title_en, text, author, hashteg)
VALUES('".$rowNews['title']."', '".$rowNews['title_en']."', '".$rowNews['text']."',
'".$rowNews['author']."', '".$rowNews['hashteg']."')";
            $result = mysql_query($addNews) or die(mysql_error());
            self::Close();
        }
}

    


Ответы

Ответ 1



Вы неправильно строки объединяете, потому и ругается. Несколько решений: Первое - встраиваем массив в строку: $a = array( 'abc' => 'csa', ); echo "arr: $a[abc]\n"; // arr: csa Т.е. никаких кавычек в массиве, никаких склеивания строк вида "строка" $param. Правда, с одинарными кавычками ('a = $a') это не работает и будет выведено все строкой (т.е. выведет a = $a). Для вашего кода: $addNews = "INSERT INTO articles (title, title_en, text, author, hashteg) VALUES('$rowNews[title]', '$rowNews[title_en]', '$rowNews[text]', '$rowNews[author]', '$rowNews[hashteg]')"; Второе - конкатенация строк: $a = 'qwerty'; echo "a = " . $a; // a = qwerty Для вашего кода: $addNews = "INSERT INTO articles (title, title_en, text, author, hashteg) VALUES('" . $rowNews['title'] . "', '" . $rowNews['title_en'] . "', '" . $rowNews['text'] . "', '" . $rowNews['author'] . "', '" . $rowNews['hashteg'] . "')"; Третье - использование sprintf: $a = 'qwe'; $b = 'asd'; sprintf('a = %s, b = %s and a = %s', $a, $b, $a); // a = qwe, b = asd and a = qwe sprintf('a = %1$s, b = %2$s and a = %1$s', $a, $b); // a = qwe, b = asd and a = qwe Для вашего кода: $sql = "INSERT INTO articles (title, title_en, text, author, hashteg) VALUES('%s', '%s', '%s', '%s', '%s')"; $addNews = sprintf($sql, $rowNews['title'], $rowNews['title_en'], $rowNews['text'], $rowNews['author'], $rowNews['hashteg']); P.S.: для вашего кода ни один из вариантов не является правильным из-за того, что не проверяются входные значения (читайте про sql-инъекции)

Ответ 2



конкатенация строк (и строковых значений переменных) в php осуществляется точками: "какая-то строка" . "ещё строка" . $var . "и ещё строка" в приложении к вашему коду: "какая-то строка" . $rowNews['title'] . "ещё строка"

Ответ 3



$addNews = "INSERT INTO articles (title, title_en, text, author, hashteg) VALUES('".$rowNews['title']."', '".$rowNews['title_en']."', '".$rowNews['text']."', '".$rowNews['author']."', '".$rowNews['hashteg']."')"; Это самый простой вариант. Есть ещё sql инъекции. Этот вариант от них не защищает

Ответ 4



Можно писать так ... , "'{$rowNews['title_en']}'", ...

воскресенье, 1 декабря 2019 г.

Выполнить строку как участок кода в C#

#c_sharp #строки #синтаксический_анализ


Имеется одномерный массив типа decimal. 

Задача: Перебрать все возможные соотношения значений массива с целью поиска максимально
релевантного. Функция определения релевантности есть. Единственное, что приходит в
голову - создавать и рандомно менять ("мутировать") "генетический код" соотношения. 

Пример: необходимо x[1] + x[5] * x[7] / x[3] представлять в виде 001A005C007D003,
где трехзначные числа - номер элемента массива, а символы A,B,C,D - соответственно
+,-,* и /. Это позволит отбрасывать наименее релевантные соотношения и запускать в
"мутацию" более релевантные.

Вопрос: понятно, что сформировать строку исполняемого кода из такого "генетического
кода" (простите за тавтологию) нетрудно, но мы получим просто переменную типа string,
и как ее потом выполнить прямо в программе? И возможно ли это?
    


Ответы

Ответ 1



Хей, давно что-то никто не писал парсеров. Давайте-ка разомнёмся и напишем. Итак, для начала структура данных, которая описывает части выражения. Это стандартная реализация discriminated union: class Node { } class Index : Node { public int Value { get; set; } } class BinaryOperation : Node { public Node Left { get; set; } public Node Right { get; set; } public int Precedence { get; set; } public string Name { get; set; } } Теперь, лексер. Поскольку я ленивый, то для токенов не буду создавать отдельную структуру данных, а просто использую Node и его варианты. Лексер у нас получится очень простой: // вспомогательная таблица операций и их приоритетов Dictionary Operations = new Dictionary() { ['A'] = ("+", 1), ['B'] = ("-", 1), ['C'] = ("*", 2), ['D'] = ("/", 2) }; // сам лексер IEnumerable Tokenize(string s) { int i = 0; while (i < s.Length) { switch (s[i]) { // это операция? создаём узел операции case char c when Operations.ContainsKey(c): (var name, var prec) = Operations[c]; yield return new BinaryOperation() { Precedence = prec, Name = name }; i++; break; // это число, создаём узел индекса case char c when char.IsDigit(c): if (i + 2 >= s.Length || !int.TryParse(s.Substring(i, 3), out var num)) throw new FormatException(); yield return new Index() { Value = num }; i += 3; break; default: throw new FormatException(); } } } С лексером всё. Дальше, парсер. Для построения синтаксического дерева из арифметического выражения воспользуемся классическим алгоритмом сортировочной станции. Node Scan(string s) { Stack output = new Stack(); Stack operations = new Stack(); void Shift() { var op = operations.Pop(); op.Right = output.Pop(); op.Left = output.Pop(); output.Push(op); } foreach (var token in Tokenize(s)) { switch (token) { case Index i: output.Push(i); break; case BinaryOperation bin: while (operations.Count > 0 && operations.Peek().Precedence >= bin.Precedence) Shift(); operations.Push(bin); break; } } while (operations.Count > 0) Shift(); return output.Single(); } Отлично, теперь бекэнд нашего компилятора. Скомпилируем Node в LINQ Expression, чтобы потом скомпилировать в нормальную функцию. Здесь тоже никаких проблем. Единственная хитрость — нам нужен «общий» параметр p. using System.Linq; using System.Linq.Expressions; Expression> Build(Node n) { var p = Expression.Parameter(typeof(int[]), "arr"); return BuildRec(n); Expression> BuildRec(Node node) { switch (node) { case Index idx: return Expression.Lambda>( Expression.ArrayAccess(p, Expression.Constant(idx.Value)), p); case BinaryOperation bin: var l = BuildRec(bin.Left); var r = BuildRec(bin.Right); var op = GetOp(bin.Name); return Expression.Lambda>(op(l.Body, r.Body), p); default: throw new ArgumentException(); } } Func GetOp(string name) { switch (name) { case "+": return Expression.Add; case "-": return Expression.Subtract; case "*": return Expression.Multiply; case "/": return Expression.Divide; default: throw new ArgumentException(); } } } Готово, проверяем! // входная строка var s = "001A005C007D003"; // получаем функцию var node = Scan(s); var func = Build(node).Compile(); // данные для функции var array = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var result = func(array); Получаем результат 12, как и ожидалось. Проверку ошибок добавьте по вкусу.

Ответ 2



Если вы хотите в самом сложном случае использовать базовые арифметические операции, то нет смысла городить код сложными конструкциями. Представим, что у вас есть массив чисел с плавающей точкой и выражение типа x[1] + x[5] * x[7] / x[3] Для начала можно заменить все переменные значениями из массива string SetValues(string expression, double[] array) { var result = expression; var regex = new Regex("x\\[(:?[0-9]+)\\]", RegexOptions.Compiled); foreach (Match match in regex.Matches(expression)) { var ind = int.Parse(match.Groups[1].Value); result = result.Replace(match.Value, array[ind] .ToString(CultureInfo.InvariantCulture)); } return result; } Получим что то вроде 9.04579045206578 + 3.56569983231169 * 5.23985354939469 / 9.11708742339028 Теперь посчитаем выражение (старый добрый трюк с DataTable) double Eval(string input) { return Convert.ToDouble(new DataTable().Compute(input, null)); } В итоге пользоваться можно этой штукой как то так: void Main() { // Генерим массив со случайными числами var random = new Random(); var data = Enumerable.Range(0, 10).Select(x=>random.NextDouble()*10).ToArray(); // Наше выражение var expression = "x[1] + x[5] * x[7] / x[3]"; // результат, который ожидаем (для проверки) var realResult = data[1] + data[5] * data[7] / data[3]; // Тут заменяем переменные значениями var expressionToEvaluate = SetValues(expression, data); // Тут наш вычисленный результат var result = Eval(expressionToEvaluate); // выводим все в консоль Console.WriteLine($"real:{realResult}"); Console.WriteLine($"calculated:{result}"); Console.WriteLine($"from {expressionToEvaluate}"); } Получим в консоли real: 11,0951011644409 calculated: 11,0951011644409 from 9.04579045206578 + 3.56569983231169 * 5.23985354939469 / 9.11708742339028 Подробнее, что можно себе позволить использовать в выражении, описано тут https://msdn.microsoft.com/ru-ru/library/system.data.datacolumn.expression(v=vs.110).aspx

Ответ 3



Можно воспользоваться пакетом System.Linq.Dynamic: var fn = DynamicExpression.ParseLambda("2+3").Compile(); Console.WriteLine(fn(null)); // 5 Передачу параметров с известными именами организовать тоже несложно: class Params { public int[] x; } var fn = DynamicExpression.ParseLambda("x[1] + x[5] * x[7] / x[3]").Compile(); Console.WriteLine(fn(new Params { x = ... } )); Однако, если вы собираетесь сначала формировать строку, а потом ее исполнять - лучше так не делайте! Гораздо проще сразу сформировать нужное AST минуя стадию строки (и System.Linq.Dynamic не понадобится): var x = Expression.Parameter(typeof(int[])); var body = Expression.Add( Expression.ArrayIndex(x, Expression.Constant(1)), Expression.Divide( Expression.Multiply( Expression.ArrayIndex(x, Expression.Constant(5)), Expression.ArrayIndex(x, Expression.Constant(7)) ), Expression.ArrayIndex(x, Expression.Constant(3)) ) ); var fn = Expression.Lambda>(body, x).Compile(); Console.WriteLine(fn(...));

воскресенье, 24 ноября 2019 г.

Как распарсить HTML в .NET?


Необходимо извлечь все URL из атрибутов href тегов a в HTML странице. Я попробова
воспользоваться регулярными выражениями:

Uri uri = new Uri("http://google.com/search?q=test");
Regex reHref = new Regex(@"]+href=""([^""]+)""[^>]+>");
string html = new WebClient().DownloadString(uri);
foreach (Match match in reHref.Matches(html))
    Console.WriteLine(match.Groups[1].ToString());


Но возникает множество потенциальных проблем:


Как отфильтровать только специфические ссылки, например, по CSS классу?
Что будет, если кавычки у атрибута другие?
Что будет, если вокруг знака равенства пробелы?
Что будет, если кусок страницы закомментирован?
Что будет, если попадётся кусок JavaScript?
И так далее.


Регулярное выражение очень быстро становится монструозным и нечитаемыми, а проблемны
мест обнаруживается всё больше и больше.

Что делать?
    


Ответы

Ответ 1



Регулярные выражения предназначены для обработки относительно простых текстов, которы задаются регулярными языками. Регулярные выражения со времени своего появления сильн усложнились, особенно в Perl, реализация регулярных выражений в котором является вдохновение для остальных языков и библиотек, но регулярные выражения всё ещё плохо приспособлен (и вряд ли когда-либо будут) для обработки сложных языков типа HTML. Сложность обработк HTML заключается ещё и в очень сложных правилах обработки невалидного кода, которы достались по наследству от первых реализаций времён рождения Интернета, когда никаки стандартов не было и в помине, а каждый производитель браузеров нагромождал уникальны и неповторимые возможности. Итак, в общем случае регулярные выражения — не лучший кандидат для обработки HTML Обычно разумнее использовать специализированные парсеры HTML. CsQuery Лицензия: MIT Один из современных парсеров HTML для .NET. В качестве основы взят парсер validator.n для Java, который в свою очередь является портом парсера из движка Gecko (Firefox) Это гарантирует, что парсер будет обрабатывать код точно так же, как современные браузеры. API черпает вдохновение у jQuery, для выбора элементов используется язык селекторо CSS. Названия методов скопированы практически один-в-один, то есть для программистов знакомых с jQuery, изучение будет простым. Обладает высокой производительностью. На порядки превосходит HtmlAgilityPack+Fizzle по скорости на сложных запросах. CQ cq = CQ.Create(html); foreach (IDomObject obj in cq.Find("a")) Console.WriteLine(obj.GetAttribute("href")); Если требуется более сложный запрос, то код практически не усложняется: CQ cq = CQ.Create(html); foreach (IDomObject obj in cq.Find("h3.r a")) Console.WriteLine(obj.GetAttribute("href")); HtmlAgilityPack Лицензия: Ms-PL Самый старый, и потому самый популярный парсер для .NET. Однако возраст не означае качество, например, уже пять лет (!!!) висит незакрытым критический баг Incorrect parsin of HTML4 optional end tags, который приводит к некорректной обработке тегов HTML, закрывающи теги для которых опциональны. В API присутствуют странности, например, если ничего н найдено, возвращается null, а не пустая коллекция. Для выбора элементов используется язык XPath, а не селекторы CSS. На простых запроса код получается более-менее удобочитаемый: HtmlDocument hap = new HtmlDocument(); hap.LoadHtml(html); HtmlNodeCollection nodes = hap.DocumentNode.SelectNodes("//a"); if (nodes != null) foreach (HtmlNode node in nodes) Console.WriteLine(node.GetAttributeValue("href", null)); Однако если нужны сложные запросы, то XPath оказывается не очень приспособленны для имитации CSS селекторов: HtmlDocument hap = new HtmlDocument(); hap.LoadHtml(html); HtmlNodeCollection nodes = hap.DocumentNode.SelectNodes( "//h3[contains(concat(' ', @class, ' '), ' r ')]/a"); if (nodes != null) foreach (HtmlNode node in nodes) Console.WriteLine(node.GetAttributeValue("href", null)); Fizzler Лицензия: LGPL Надстройка к HtmlAgilityPack, позволяющая использовать селекторы CSS. HtmlDocument hap = new HtmlDocument(); hap.LoadHtml(html); foreach (HtmlNode node in hap.DocumentNode.QuerySelectorAll("h3.r a")) Console.WriteLine(node.GetAttributeValue("href", null)); AngleSharp Лицензия: BSD (3-clause) Новый игрок на поле парсеров. В отличие от CsQuery, написан с нуля вручную на C# Также включает парсеры других языков. API построен на базе официальной спецификации по JavaScript HTML DOM. В некоторы местах есть странности, непривычные для разработчиков на .NET (например, при обращени к неверному индексу в коллекции будет возвращён null, а не выброшено исключение; ест свой отдельный класс Url; пространства имён очень гранулярные, даже базовое использовани библиотеки требует три using и т. п.), но в целом ничего критичного. Из других странностей — библиотека тащит за собой Microsoft BCL Portability Pack Поэтому, когда подключите AngleSharp через NuGet, не удивляйтесь, если обнаружите подключенным три дополнительных пакета: Microsoft.Bcl, Microsoft.Bcl.Build, Microsoft.Bcl.Async. Обработка HTML простая: IHtmlDocument angle = new HtmlParser(html).Parse(); foreach (IElement element in angle.QuerySelectorAll("a")) Console.WriteLine(element.GetAttribute("href")); Она не усложняется, и если нужна более сложная логика: IHtmlDocument angle = new HtmlParser(html).Parse(); foreach (IElement element in angle.QuerySelectorAll("h3.r a")) Console.WriteLine(element.GetAttribute("href")); Regex Страшные и ужасные регулярные выражения. Применять их нежелательно, но иногда возникае необходимость, так как парсеры, которые строят DOM, заметно прожорливее, чем Regex они потребляют больше и процессорного времени, и памяти. Если дошло до регулярных выражений, то нужно понимать, что вы не сможете построит на них универсальное и абсолютно надёжное решение. Однако если вы хотите парсить конкретны сайт, то эта проблема может быть не так критична. Ради всего святого, не надо превращать регулярные выражения в нечитаемое месиво Вы не пишете код на C# в одну строчку с однобуквенными именами переменных, так и регулярны выражения не нужно портить. Движок регулярных выражений в .NET достаточно мощный, чтоб можно было писать качественный код. Например, вот немного доработанный код для извлечения ссылок из вопроса: Regex reHref = new Regex(@"(?inx) ]* href \s* = \s* (? ['""] ) (? [^""]+ ) \k [^>]* >"); foreach (Match match in reHref.Matches(html)) Console.WriteLine(match.Groups["url"].ToString());

Ответ 2



Используйте библиотеку CefSharp для решения подобных задач. Почему следует применять именно такой подход? У вас намного упрощается процесс разработки за счёт того, что вместо написания XPath, условий и/или циклов в C# вы просто в консоли браузера (желательно основанного на Chromium) просто разрабатываете всё что вам нужно, затем когда уже написан небольшой костяк из класса (покажу его ниже), вы просто вставляете JavaScript-код, который вам нужен. Надёжность. Вы не пытаетесь парсить HTML и не изобретаете велосипед, что являетс почти всегда очень плохой идеей. Проект основан на Chromium, поэтому вам не приходитс доверять какому-то новому/незнакомому продукту. Активно поддерживается для синхронизаци с новой версией. Для Javascript-обращений для простоты и демонстрации используется jQuery, предполагая что на целевом сайте он тоже есть. Но это может быть также чистый JavaScript либо друга библиотека при условии, что эта библиотека используется на сайте. Если вы проскроллите вниз, то заметите, что помимо написания небольшой прослойк кода и инициалиации, решение занимает одну-две строки: string[] urls = await wrapper.GetResultAfterPageLoad("https://yandex.ru", async () => await wrapper.EvaluateJavascript( "$('a[href]').map((index, element) => $(element).prop('href')).toArray()")); Что это такое? Это управляемая оболочка над CEF (Chromium Embedded Framework). То есть Вы получает мощь Chromium, которой управляете программно. Почему именно CEF/CefSharp? Не стоит заморачиваться парсингом страниц (а это сложная и неблагодарная задача которую крайне не рекомендую делать). Можно работать с уже загруженной страницей (после выполнения скриптов). Есть возможность выполнять произвольный JavaScript с последними возможностями. Даёт возможность вызывать AJAX с помощью JavaScript, а затем при успехе (success) дёргать события в C#-коде с результатом AJAX. Подробно и с примером рассмотрел здесь. Разновидности CefSharp CefSharp.WinForms CefSharp.Wpf CefSharp.OffScreen Первые две используются если вам надо дать пользователям элемент управления "Браузер" Концептуально похоже на WebBrowser в Windows Forms, который является оболочкой для управлени IE, а не Chromium, как в нашем случае. Поэтому мы будем использовать CefSharp.OffScreen (закадровую) разновидность. Написание кода Допустим у нас консольное приложение, но это уже зависит от Вас. Устанавливаем Nuget-пакет CefSharp.OffScreen 57-ой версии: Install-Package CefSharp.OffScreen -Version 57.0.0 Дело в том, что C# всё массивы маппает к List, результат JavaScript обёрну в object, в котором уже содержатся List, string, bool, int в зависимости о результата. Для того чтобы сделать результаты строго типизированными, создаём небольшо ConvertHelper: public static class ConvertHelper { public static T[] GetArrayFromObjectList(object obj) { return ((IEnumerable)obj) .Cast() .ToArray(); } public static List GetListFromObjectList(object obj) { return ((IEnumerable)obj) .Cast() .ToList(); } public static T ToTypedVariable(object obj) { if (obj == null) { dynamic dynamicResult = null; return dynamicResult; } Type type = typeof(T); if (type.IsArray) { dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetArrayFromObjectList)) .MakeGenericMethod(type.GetElementType()) .Invoke(null, new[] { obj }); return dynamicResult; } if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) { dynamic dynamicResult = typeof(ConvertHelper).GetMethod(nameof(GetListFromObjectList)) .MakeGenericMethod(type.GetGenericArguments().Single()) .Invoke(null, new[] { obj }); return dynamicResult; } return (T)obj; } } Для обработки с ошибками Javascript создаём класс JavascriptException. public class JavascriptException : Exception { public JavascriptException(string message) : base(message) { } } У вас может быть свой способ обработки ошибок. Создаём класс CefSharpWrapper: public sealed class CefSharpWrapper { private ChromiumWebBrowser _browser; public void InitializeBrowser() { Cef.EnableHighDPISupport(); // Perform dependency check to make sure all relevant resources are in ou output directory. Cef.Initialize(new CefSettings(), performDependencyCheck: false, browserProcessHandler null); _browser = new ChromiumWebBrowser(); // wait till browser initialised AutoResetEvent waitHandle = new AutoResetEvent(false); EventHandler onBrowserInitialized = null; onBrowserInitialized = (sender, e) => { _browser.BrowserInitialized -= onBrowserInitialized; waitHandle.Set(); }; _browser.BrowserInitialized += onBrowserInitialized; waitHandle.WaitOne(); } public void ShutdownBrowser() { // Clean up Chromium objects. You need to call this in your applicatio otherwise // you will get a crash when closing. Cef.Shutdown(); } public Task GetResultAfterPageLoad(string pageUrl, Func> onLoadCallback) { TaskCompletionSource tcs = new TaskCompletionSource(); EventHandler onPageLoaded = null; T t = default(T); // An event that is fired when the first page is finished loading. // This returns to us from another thread. onPageLoaded = async (sender, e) => { // Check to see if loading is complete - this event is called twice one when loading starts // second time when it's finished // (rather than an iframe within the main frame). if (!e.IsLoading) { // Remove the load event handler, because we only want one snapsho of the initial page. _browser.LoadingStateChanged -= onPageLoaded; t = await onLoadCallback(); tcs.SetResult(t); } }; _browser.LoadingStateChanged += onPageLoaded; _browser.Load(pageUrl); return tcs.Task; } public async Task EvaluateJavascript(string script) { JavascriptResponse javascriptResponse = await _browser.GetMainFrame().EvaluateScriptAsync(script); if (!javascriptResponse.Success) { throw new JavascriptException(javascriptResponse.Message); } } public async Task EvaluateJavascript(string script) { JavascriptResponse javascriptResponse = await _browser.GetMainFrame().EvaluateScriptAsync(script); if (javascriptResponse.Success) { object scriptResult = javascriptResponse.Result; return ConvertHelper.ToTypedVariable(scriptResult); } throw new JavascriptException(javascriptResponse.Message); } } Далее вызываем наш класс CefSharpWrapper из метода Main. public class Program { private static void Main() { MainAsync().Wait(); } private static async Task MainAsync() { CefSharpWrapper wrapper = new CefSharpWrapper(); wrapper.InitializeBrowser(); string[] urls = await wrapper.GetResultAfterPageLoad("https://yandex.ru" async () => await wrapper.EvaluateJavascript("$('a[href]').map((index element) => $(element).prop('href')).toArray()")); wrapper.ShutdownBrowser(); } } Также: в данной библиотеке есть особенность, что пустой JavaScript-массив приводитс к null. Поэтому, возможно, есть смысл добавить в ConvertHelper соотвествующий код (эт зависит от вашего кода и потребностей), либо в вызывающем коде писать что-то вроде if (urls == null) urls = new string[0] Также установите x64 или x86 в качестве платформы. Платформа Any CPU поддерживается но требует дополнительного кода.

Ответ 3



Если требования к производительности не очень высокие, можно использовать COM-объек Internet Explorer (добавить ссылку на Microsoft HTML Object Library): public static List ParseLinks(string html) { List res = new List(); mshtml.HTMLDocument doc = null; mshtml.IHTMLDocument2 d2 = null; mshtml.IHTMLDocument3 d = null; try { doc = new mshtml.HTMLDocument();//инициализация IE d2 = (mshtml.IHTMLDocument2)doc; d2.write(html); d = (mshtml.IHTMLDocument3)doc; var coll = d.getElementsByTagName("a");//получить коллекцию элементов п имени тега object val; foreach (mshtml.IHTMLElement el in coll)//извлечь атрибут href из всех элементов { val=el.getAttribute("href"); if (val == null) continue; res.Add(val.ToString()); } } finally { //освобождение ресурсов if (doc != null) Marshal.ReleaseComObject(doc); if (d2 != null) Marshal.ReleaseComObject(d2); if (d != null) Marshal.ReleaseComObject(d); } return res; }

Ответ 4



Вставлю свои пять копеек, если нет желания возиться с COM-объектами mshtml, можн создать объект WebBrowser() из Windows.Forms, причём, если вам не нужно срабатывани всех скриптов, то, я так понимаю, страницу можно грузить не самим браузером, а чем попроще вроде WebClient.DownloadString(), а далее загружаем полученный текст страницы для парсинг в WebBrowser: var itemPageText = _webClient.DownloadString(url); using (var pageHtml = new WebBrowser()) { pageHtml.DocumentText = itemPageText; var elem = pageHtml.Document.GetElementById("imainImgHldr"); } ну и т.п., главное, что методы вроде GetElementById() тоже представляют собой нескольк более удобоваримые обёртки в отличие от mshtml.

Ответ 5



F# Поиск на странице всех ссылок на книги по F#: let fsys = "https://www.google.com/search?tbm=bks&q=F%23" let doc2 = HtmlDocument.Load(fsys) let books = doc2.CssSelect("div.g h3.r a") |> List.map(fun a -> a.InnerText().Trim(), a.AttributeValue("href")) |> List.filter(fun (title, href) -> title.Contains("F#")) F# Data F# Data HTML Parser F# Data HTML CSS selectors

Ответ 6



У меня все замечательно получается при помощи XElement Попробуйте :) var htmlDom = XElement.Parse("[Код HTML]"); Как подсказали в комментариях, это будет работать если нужная нам страница являетс валидным XHTML документом.

вторник, 16 июля 2019 г.

Создание аннотации в выдаче поискового запроса при отсутствии тега description

У многих страниц при парсинге, отсутствует тег description. Здесь написано, что при отсутствии, тега description поисковые системы берут отдельный отрывок. Не до конца понятно, по-какому принципу отбирается аннотация к сайту. К примеру, если выбирать самый первый встречающийся тег

может попасться не то описание. У википедии нет тега description, хотя Google в таком случае выдает по запросу python правильный абзац. Можно ли это как-то сделать при помощи парсера или регулярок ?


Ответ

Думаю вы не дочитали описание meta-тега description: При отсутствии тега поисковые системы выдают в аннотации первую строку документа или отрывок, содержащий ключевые слова. То есть поисковик берет первый отрывок содержащий ключевое слово заданное в поиске

суббота, 6 июля 2019 г.

Синтаксический анализатор (построение грамматики) ?

Нужно построить синтаксический анализатор. В частности для языка создать контекстно-зависимую грамматику( LL) Везде встречаются примеры только для чисел и знаков, у меня присутствуют только 4 буквы(A, B, C, D) и для них надо построить грамматику . Есть ли примеры построения грамматики ?


Ответ

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

четверг, 6 июня 2019 г.

Парсинг XML

Есть xml файл test.xml, в котором содержится: 1 20121126 03:57:10 test Rxxx WMIDxxx 0 3 95.28.75.19 null null -1 0 Вот как, средствами php, получить значение amount?


Ответ

$xml = new DomDocument; $xml -> ValidateOnParse = true; $xml -> load('test.xml'); $amount = $xml -> getElementsByTagName('amount')->item(0)->nodeValue;