- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有一些纯文本和 html。我需要创建一个将返回相同 html 的 PHP 方法,但带有 <span class="marked">
在任何文本实例和 </span>
之前之后。
请注意,它应该支持 html 中的标签(例如,如果文本是 blabla
,那么它应该标记为 bla<b>bla</b>
或 <a href="http://abc.com">bla</a>bla
。
它应该区分大小写并支持长文本(多行等)。
例如,如果我用文本“my name is josh”和以下 html 调用此函数:
<html>
<head>
<title>My Name Is Josh!!!</title>
</head>
<body>
<h1>my name is <b>josh</b></h1>
<div>
<a href="http://www.names.com">my name</a> is josh
</div>
<u>my</u> <i>name</i> <b>is</b> <span style="font-family: Tahoma;">Josh</span>.
</body>
</html>
...它应该返回:
<html>
<head>
<title><span class="marked">My Name Is Josh</span>!!!</title>
</head>
<body>
<h1><span class="marked">my name is <b>josh</b></span></h1>
<div>
<span class="marked"><a href="http://www.names.com">my name</a> is josh</span>
</div>
<span class="marked"><u>my</u> <i>name</i> <b>is</b> <span style="font-family: Tahoma;">Josh</span></span>.
</body>
</html>
谢谢。
最佳答案
这会很棘手。
虽然您可以通过简单的正则表达式黑客攻击来做到这一点,但忽略标签内的任何内容,就像天真一样:
preg_replace(
'My(<[^>]>)*\s+(<[^>]>)*name(<[^>]>)*\s+(<[^>]>)*is(<[^>]>)*\s+(<[^>]>)*Josh',
'<span class="marked">$0</span>', $html
)
这一点都不可靠。部分原因是 HTML 不能用正则表达式解析:输入 >
是有效的在属性值中,其他非元素构造如注释将被错误解析。即使使用更严格的表达式来匹配标签⟩—⟩像<[^>\s]*(\s+([^>\s]+(\s*=\s*([^"'\s>][\s>]*|"[^"]*"|'[^']*')\s*))?)*\s*\/?>
这样非常笨拙的东西,您仍然会遇到许多相同的问题,尤其是在不能保证输入 HTML 有效的情况下。
这甚至可能是一个安全问题,就好像您正在处理的 HTML 不受信任,它可能会欺骗您的解析器将文本内容转换为属性,从而导致脚本注入(inject)。
但即使忽略这一点,您也无法确保正确的元素嵌套。所以你可能会:
<em>My name is <strong>Josh</strong>!!!</em>
进入错误嵌套和无效:
<span class="marked"><em>My name is <strong>Josh</strong></span>!!!</em>
或:
My
<table><tr><td>name is</td></tr></table>
Josh
那些元素不能用 span 包裹的地方。如果你不走运,浏览器修复程序来“纠正”你的无效输出可能最终会留下一半的页面“标记”,或者弄乱页面布局。
因此,您必须在已解析的 DOM 级别上执行此操作,而不是使用字符串破解。您可以使用 PHP 解析整个字符串,处理它并重新序列化,但如果从可访问性的角度来看它是可以接受的,那么在浏览器端用 JavaScript 执行它可能会更容易,因为内容已经被解析为DOM 节点。
还是挺难的。 This question在文本全部位于同一个文本节点内的情况下处理它,但这是一个简单得多的情况。
您实际上必须做的是:
for each Element that may contain a <span>:
for each child node in the element:
generate the text content of this node and all following siblings
match the target string/regex against the whole text
if there is no match:
break the outer loop - on to the next element.
if the current node is an element node and the index of the match is not 0:
break the inner loop - on to the next sibling node
if the current node is a text node and the index of the match is > the length of the Text node data:
break the inner loop - on to the next sibling node
// now we have to find the position of the end of the match
n is the length of the match string
iterate through the remaining text node data and sibling text content:
compare the length of the text content with n
less?:
subtract length from n and continue
same?:
we've got a match on a node boundary
split the first text node if necessary
insert a new span into the document
move all the nodes from the first text node to this boundary inside the span
break to outer loop, next element
greater?:
we've got a match ending inside the node.
is the node a text node?:
then we can split the text node
also split the first text node if necessary
insert a new span into the document
move all contained nodes inside the span
break to outer loop, next element
no, an element?:
oh dear! We can't insert a span here
哎呀。
如果可以接受单独包装作为匹配一部分的每个文本节点,那么这里有一个稍微不那么讨厌的替代建议。所以:
<p>Oh, my</p> name <div><div>is</div><div> Josh
会给你留下输出:
<p>Oh, <span class="marked">my</span></p>
<span class="marked"> name </span>
<div><div><span class="marked">is</span></div></div>
<span class="marked"> Josh</span>
这可能看起来不错,具体取决于您如何设置火柴的样式。它还可以解决部分匹配元素内部的错误嵌套问题。
ETA:哦,该死的伪代码,我现在或多或少已经写了代码,不妨完成它。这是后一种方法的 JavaScript 版本:
markTextInElement(document.body, /My\s+name\s+is\s+Josh/gi);
function markTextInElement(element, regexp) {
var nodes= [];
collectTextNodes(nodes, element);
var datas= nodes.map(function(node) { return node.data; });
var text= datas.join('');
// Get list of [startnodei, startindex, endnodei, endindex] matches
//
var matches= [], match;
while (match= regexp.exec(text)) {
var p0= getPositionInStrings(datas, match.index, false);
var p1= getPositionInStrings(datas, match.index+match[0].length, true);
matches.push([p0[0], p0[1], p1[0], p1[1]]);
}
// Get list of nodes for each match, splitted at the edges of the
// text. Reverse-iterate to avoid the splitting changing nodes we
// have yet to process.
//
for (var i= matches.length; i-->0;) {
var ni0= matches[i][0], ix0= matches[i][1], ni1= matches[i][2], ix1= matches[i][3];
var mnodes= nodes.slice(ni0, ni1+1);
if (ix1<nodes[ni1].length)
nodes[ni1].splitText(ix1);
if (ix0>0)
mnodes[0]= nodes[ni0].splitText(ix0);
// Replace each text node in the sublist with a wrapped version
//
mnodes.forEach(function(node) {
var span= document.createElement('span');
span.className= 'marked';
node.parentNode.replaceChild(span, node);
span.appendChild(node);
});
}
}
function collectTextNodes(texts, element) {
var textok= [
'applet', 'col', 'colgroup', 'dl', 'iframe', 'map', 'object', 'ol',
'optgroup', 'option', 'script', 'select', 'style', 'table',
'tbody', 'textarea', 'tfoot', 'thead', 'tr', 'ul'
].indexOf(element.tagName.toLowerCase()===-1)
for (var i= 0; i<element.childNodes.length; i++) {
var child= element.childNodes[i];
if (child.nodeType===3 && textok)
texts.push(child);
if (child.nodeType===1)
collectTextNodes(texts, child);
};
}
function getPositionInStrings(strs, index, toend) {
var ix= 0;
for (var i= 0; i<strs.length; i++) {
var n= index-ix, l= strs[i].length;
if (toend? l>=n : l>n)
return [i, n];
ix+= l;
}
return [i, 0];
}
// We've used a few ECMAScript Fifth Edition Array features.
// Make them work in browsers that don't support them natively.
//
if (!('indexOf' in Array.prototype)) {
Array.prototype.indexOf= function(find, i /*opt*/) {
if (i===undefined) i= 0;
if (i<0) i+= this.length;
if (i<0) i= 0;
for (var n= this.length; i<n; i++)
if (i in this && this[i]===find)
return i;
return -1;
};
}
if (!('forEach' in Array.prototype)) {
Array.prototype.forEach= function(action, that /*opt*/) {
for (var i= 0, n= this.length; i<n; i++)
if (i in this)
action.call(that, this[i], i, this);
};
}
if (!('map' in Array.prototype)) {
Array.prototype.map= function(mapper, that /*opt*/) {
var other= new Array(this.length);
for (var i= 0, n= this.length; i<n; i++)
if (i in this)
other[i]= mapper.call(that, this[i], i, this);
return other;
};
}
关于php - 在 HTML 中标记文本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2843773/
虽然我在 reactjs 组件(组件名称为 renderLocationLink)的渲染方法返回的 html 中包含了 a 标签的 onclick 处理程序,但渲染正确地发生了 onclick 处理程
我必须以 docx 格式存储一些文档,但无法忍受使用 msword:我想编辑某种纯文本标记,除了基于 XML 的东西(我也不喜欢那样)和从/到那个到/从 docx 转换。 有什么选择吗? 编辑:由于人
有一个页面,其 anchor 标记在延迟后变得可点击。我想使用用户脚本在可点击后点击它。 页面加载时,HTML 源代码为: Download 延迟一段时间后,#button 变
我正在将 XML 文件解析为 pandas 数据帧。使用下面的代码我可以成功获取所有内容,但是这使用了完整 XML 的编辑版本。完整的 XML 在主数据表之上有一堆摘要数据,请参阅完整的 XML he
目前我正在研究 xml.sax 解析器来解析 xml 文件 假设我有以下代码 filepath = 'users/file.xml' try: parser = xml.sax.make_pa
我正在尝试构建一种语法来解释用户输入的文本,搜索引擎风格。它将支持 AND、OR、NOT 和 ANDNOT bool 运算符。我几乎所有东西都在工作,但我想添加一个规则,将引用字符串之外的两个相邻关键
我遇到了 Terraform EKS 标记的问题,并且似乎没有找到可行的解决方案来在创建新集群时标记所有 VPC 子网。 提供一些上下文:我们有一个 AWS VPC,我们在其中将多个 EKS 集群部署
我是xpath的新手,对此了解不多。我知道有一种方法可以使用xpath在xml / xhtml文件中查找特定标签。就我而言,我试图找到第一个(a)链接元素。不幸的是,我的xpath字符串[// a [
我在索引页上的产品卡上遇到问题。在产品卡内部,我有 Vue 组件来渲染表单(数量和添加到购物车按钮)。当我单击“添加到购物车”按钮时,我得到了预期的结果。响应被发送到根 vue 组件,然后我看到产品已
html setMouse(true)} onMouseEnter={() => setMouse(false)} className='resume-container'> CSS .resum
我在组件中有一组枚举,如下所示: type TOption = (clVisible, clVisibleAlways, clRenderable, clEditable); TOptions
是否有出于性能考虑的javadoc标签? 人们可以想象: /** * ...other javadoc tags... * @perform Expected to run in O(n) tim
html setMouse(true)} onMouseEnter={() => setMouse(false)} className='resume-container'> CSS .resum
我有一个包含多个小子图的图。目标是当且仅当子图中的所有节点都是蓝色时,才将子图中的所有蓝色节点标记为红色。如果子图中的一个节点具有不同的颜色,绿色,那么我们将不会更改该子图中节点的颜色。 这是我正在使
我正在使用 json-ld 开发事件标记以包含在确认电子邮件中。 我的一些事件会定期重复发生。但是,最新的 Schema.org 规范不支持重复发生的事件,因此我遵循了此处提供的建议:http://l
我创建了一个插件,可以添加带有相应行号的标记。现在,这很棒,因为它现在显示在“标记” View 中。有没有办法当我双击标记上的一行时,它会转到标记指示的行? 谢谢。 最佳答案 双击“标记” View
是否有一个插件具有与 Facebook 标记类似的行为? 它的特别之处在于它具有: 在键入的单词之间自动完成 特殊输出的 html(与另一个输入字段同步) 最佳答案 您可以使用jquery提及输入pl
有没有更好的方法来读取java文件中的 token ?我目前正在使用 StringTokenizer 来分割 token 。但在大多数情况下,它的效率可能非常低,因为您必须逐个 token 地读取 t
我想知道是否有某种方法可以标记文件来识别该文件是否包含x。 考虑以下示例: 在批量转换过程中,我正在创建一个日志文件,其中列出了各个转换的成功/失败。 所以流程如下: 开始转换过程 创建名为batch
我一直在尝试模拟点击标签,但这并没有像我需要的那样工作。我的 anchor 标记看起来像这样 Download this pic 正常的 $("a").click() 或 trigger('cli
我是一名优秀的程序员,十分优秀!