Не анализируйте HTML с регулярным выражением! Серьезно, в общем случае это буквально невозможно. И на самом деле, вы не можете делать то, что вы хотите с помощью регулярных выражений. Это та же проблема, что и при сопоставлении сбалансированных вложенных пар скобок, за исключением того, что вы хотите сопоставить вложенные пары <title>
/ </title>
, а это не обычный язык.
( Редактировать 1: Мне пришлось пересмотреть мой ответ, поскольку я увидел, что у вас нет доступа к DOM; то, что у меня изначально было, см. Ниже.)
Итак, зачем вам это делать? Возможно, есть лучший способ. Это помечено JavaScript, но вы никогда не упоминаете об этом в своем ответе. Если у вас нет JavaScript, вероятно, вы можете использовать HTML-парсер, который, вероятно, будет лучшим выбором. Если вы используете JavaScript, он все еще может быть, но я не гуру JavaScript.
Теперь обратите внимание: наличие нескольких или вложенных тегов title
на самом деле не является допустимым HTML, поэтому не нужно беспокоиться об этом. Если это так, и если мы можем сделать еще некоторые предположения, вы можете создать вариант использования, который, вероятно, сработает. Например: без комментариев, без CDATA
блоков и т. Д. (Хотя вы могли бы справиться с ними, потому что они не могут вкладываться.) Но могут быть крайние случаи, которые я забыл! Кроме того, ни Safari, ни Firefox не рассматривали ваш третий случай как вложенные теги заголовков, вместо этого просматривая его как один тег заголовка, содержащий буквенную строку Title of the document <title> Continuing title
. Таким образом, если вы можете игнорировать этот случай, может иметь возможность взломать хрупкий набор регулярных выражений, которые будут работать. Возможно (слегка проверено!) Что-то вроде этого:
// Edit 2: Made this function case-insensitive where it needed to be.
// Edit 3: Used substring() instead of replace() to remove the extraneous
// title tags and fixed the "not matching" case.
function getTitle(html) {
return (html.replace( /<!\[CDATA\[(.+?)\]\]>/g
, function (_match, body) {
return body.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
} )
.replace(/<!--.+?-->/g, '')
.match(/<title>.+?<\/title>/ig) || [])
.map(function (t) { return t.substring(7, t.length - 8) })
.join(' ')
}
Я не гуру HTML, поэтому я, вероятно, пропустил пару крайних случаев, но вот что это делает. Сначала мы находим каждый раздел CDATA . Мы берем его внутренности и превращаем каждого нелегального персонажа в эквивалент его сущности, и избавляемся от <![CDATA[
и ]]>
. Далее мы удаляем каждый комментарий. После этого мы сопоставляем каждый заголовок и получаем массив совпадений (получение массива совпадений несовместимо с извлечением подгрупп), в случае, если мы находимся в случае неверных кратных title
s. Редактировать 3: Затем мы проверяем, ничего не найдено, в этом случае .match()
возвращает null
, и вместо этого возвращаем []
, если это так; таким образом, у нас всегда есть массив. Затем мы обрезаем теги от начала и до конца ( edit 3: , больше не использующих regexen для этого шага), и, наконец, строим каждый фрагмент заголовка вместе с пробелом. Это справится, я думаю , ваш случай один и случай два. Если вам нужен только юридический случай (случай один), замените последние три строки (кроме }
) одной строкой .match(/<title>(.+?)<\/title>/)[0]
. Однако, хотя это будет работать (я думаю) во многих случаях, я делаю предположения (как относительно нашего ввода ( например , все теги заголовка отображаются вместе и где вы хотите их видеть), так и о том, что мы ' ищем только один (набор) <title>...</title>
с и, вероятно, пропустили какой-то крайний случай или другой. Надеюсь, получится, что вы можете использовать более приятное решение.
Редактировать 1: Я упустил тот факт, что вам нужно работать с простым текстом; остальная часть моего первоначального ответа предполагала, что у вас есть доступ к DOM. Я оставлю это здесь для потомков, но это не особенно относится к вам.
Если у вас был доступ к DOM в JavaScript, вы могли бы сделать следующее, если бы у вас был правильный HTML с одним тегом title
:
var titles = document.getElementsByTagName('title')
var titleText = titles.length > 0 ? titles[0].text : ''
Однако, если у вас на самом деле есть HTML, который выглядит как вторые два случая, которые вы нам показали (надеюсь, нет, но вы никогда не знаете), вам придется заняться чем-то другим. Ни Firefox, ни Safari не рассматривали ваш третий случай как вложенные теги заголовков, вместо этого просматривая его как один тег заголовка, содержащий буквенную строку Title of the document <title> Continuing title
. Таким образом, если вам нужно иметь дело только с первыми двумя случаями, это будет работать:
var titles = document.getElementsByTagName('title')
var tlength = titles.length
var titleText = ''
for (var i = 0; i < tlength; ++i)
titleText += titles[i].text
А если у вас третий случай, то вам нужно удалить посторонний тег <title>
, который может быть немного хитрым, но, вероятно, это не так. Если вы знаете, что <title>
никогда не появится, кроме как из-за неправильно сформированного HTML, как описано выше, то вы можете использовать метод replace
, чтобы избавиться от него. В одноместном корпусе - <title>
, вы хотите
// Edit 2: Case-insensitivity
var titles = document.getElementsByTagName('title')
var titleText = titles.length > 0 ? titles[0].text.replace(/<title>/ig,'') : ''
В искаженном случае нескольких автономных <title>
вы хотите
// Edit 2: Case-insensitivity
var titles = document.getElementsByTagName('title')
var tlength = titles.length
var titleText = ''
for (var i = 0; i < tlength; ++i)
titleText += titles[i].text.replace(/<title>/ig,'')
Если <title>
может появиться как допустимая строка по другим причинам, то у вас проблемы; вам нужно выяснить почему это было в строке и заменить его, только если вы должны были. И, насколько я могу судить, нет хорошего общего способа сделать это. Но, надеюсь, (хотя и не обязательно) у вас есть законный HTML.