Разделитель для использования для анализа XML на основе регулярных выражений? - PullRequest
1 голос
/ 08 октября 2011

Прежде всего, я прекрасно осознаю, что пытаться писать XML-парсер вручную - ужасная идея, и что ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ TO͇̹̺ͅƝ̴ȳ̳ TH̘Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝S̨̥̫͎̭ͯ̿̔̀ͅ и т. Д.

Тем не менее, у меня есть задание, в котором я должен взять веб-страницу, вырезать теги (немного по-разному обрабатывать <p> и <a href>) и отобразить красивый текст без тегов. Мне не разрешается использовать пакет org.xml.sax или что-либо подобное.

Наш класс еще не узнал о регулярных выражениях, и большинство моих одноклассников произносят нечестивые заклинания с String.indexOf(). Мне казалось, что намного проще (не говоря уже о том, чтобы лучше) взломать основанный на событиях парсер {X, HT} ML.

Итак, у меня есть Scanner для потока веб-страницы, и я имею это (некоторые детали удалены для краткости):

stream.useDelimiter("\r?\n|\r"); // Use platform-independent newlines
                                 //as delimiter
//                 1         2      3   4      5     6          7    8    9   10
String tagRE = "([^<>]*?)(<!?\\s*)(/?)(\\s*)(\\w*)(\\s*[^<>]*?)(/?)(\\s*)(>)([^<>]*)";
//(Reluctant-anything) < whitespace optional-/ whitespace (word) whitespace
//reluctant-anything > (greedy-anything)

fireOpenFileEvent();
Pattern tagPat = Pattern.compile(tagRE);
while(stream.hasNextLine())
{
    if(stream.hasNext(tagPat))
    {
        String toParse = stream.next(tagPat);
        Matcher m = tagPat.matcher(toParse);
        if(! m.matches()) System.err.println("Impossible non-match!");

        fireTextEvent(m.group(1));
        String tag = m.group(5);
        if(! m.group(7).equals("")) //Self-closing tag
        {
            fireTagEvent(new XMLElement(tag, false));
            fireTagEvent(new XMLElement(tag, true));
        }
        else
        {
            fireTagEvent(new XMLElement(tag, m.group(3).equals("/")));
        }
        fireTextEvent(m.group(10));
    }
    else //No tags (regex doesn't match). Just plain text
    {
        fireTextEvent(stream.nextLine);
    }
}
fireEOFEvent();

Это прекрасно работает во многих случаях, кроме одного - когда в строке более одного тега. Я действительно надеялся, что Scanner не разбьет вещи на токены - и что вызов next(pattern) сожрет столько потока, сколько необходимо для соответствия. Таким образом, если строка была <b>Hello World!</b>, она соответствовала бы <b>Hello World! на одной итерации, а затем </b> в следующий раз. Вместо этого он обрабатывает строку за раз. Поскольку вся строка не соответствует шаблону, она обрабатывается предложением else. И никакие теги не удаляются.

Так какой же самый лучший подход? Есть ли какой-то магический разделитель, который я могу использовать? Должен ли я заставить регулярное выражение сопоставлять что-либо с тегом, отрубить первый тег, а затем рекурсивно обработать остальную часть строки? Должен ли я попробовать гигантский взлом и заменить каждое "<" на "\ n <"? Я вообще не на той ноге? </p>

Заранее спасибо.

Ответы [ 3 ]

1 голос
/ 09 октября 2011

Вы используете неправильную технологию.Не существует такого понятия, как «анализ на основе регулярных выражений».Синтаксический анализ и XML подразумевают стек, а регулярное выражение не имеет его.Используйте правильный синтаксический анализатор XML или XPath, как предложено @ Dabbler.

РЕДАКТИРОВАТЬ: Я пропустил часть о назначении класса.На мой взгляд, не очень продуманное задание.Вы, вероятно, не знаете о синтаксическом анализе, вы не можете использовать инструменты, предоставленные для этой цели, полученный код на самом деле вас мало учит, за исключением нечестивых заклинаний вызовов indexOf (), ... Способ сделать этоэто один символ за раз, как это было предложено другим автором: запишите символ <, начните сохранять имя тега, остановитесь на следующем пробеле или>, проигнорируйте или обработайте атрибуты, как требуется;начать обработку контента;если вы нажмете на открытие <, нажмите все состояние и перезапустите;и когда вы нажимаете на закрывающую /> всплывающее состояние.

1 голос
/ 09 октября 2011

Когда вы вызываете метод next(Pattern), вы сообщаете Сканеру, что следующий токен - это все до следующего разделителя; Единственный вопрос: токен соответствует шаблону? Это согласуется с другими nextXXX() методами (например, nextInt() завершается неудачно, если следующий токен не выглядит как int), но все ожидают, что next(Pattern) будет работать по-другому.

Я думаю, что вы ищете метод findWithinHorizon(); он игнорирует разделитель и просто находит следующее совпадение, так же как и метод find() Matcher. Попробуйте это: выбросите все эти hasNextLine(), hasNext(Pattern) вещи и используйте вместо этого этот фреймворк:

String lastHit = stream.findWithinHorizon(tagRE, 0);  // always use '0'
while (lastHit != null)
{
    MatchResult lastMatch = stream.match();

    // ...

    lastHit = stream.findWithinHorizon(tagRE, 0);
}

Заполните свой код запуска событий, настройте регулярное выражение по мере необходимости, но не используйте никакие другие методы Сканера (кроме открытия и закрытия потока, то есть). Когда вы пытаетесь сделать что-либо сложное, большая часть API-интерфейса сканера просто мешает.

API сканера может быть раздутым и неинтуитивным, но у него есть одна чрезвычайно полезная функция: при таком способе он будет продолжать чтение из потока не только до тех пор, пока не найдет совпадение, но и до тех пор, пока не будет уверен, что больше не будет совпадение возможно с той же стартовой позиции. Другими словами, он работает так же, как метод Matcher find() со статической строкой. Из всех других известных мне регулярных выражений только Boost предлагает что-то похожее.

0 голосов
/ 08 октября 2011

Обязательно ли вы используете RegEx или XPath / XSLT является опцией?Затем, если вы вводите XML (или XHTML, в этом отношении), все, что вам нужно сделать, это преобразовать весь ввод в строку.Это исключит все теги и атрибуты, оставив только текстовое содержимое элементов.

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