在大型html文档中为图像添加缺少的alt标签的最有效方法

时间:2011-09-23 15:36:32

标签: c# html accessibility

为了符合可访问性标准,我需要确保一些动态生成的html(我不能控制)中的所有图像都有一个空的alt标记(如果没有指定)。

示例输入:

<html>
    <body>
          <img src="foo.gif" />
          <p>Some other content</p>
          <img src="bar.gif" alt="" />
          <img src="blah.gif" alt="Blah!" />
    </body>
</html>

期望的输出:

<html>
    <body>
          <img src="foo.gif" alt="" />
          <p>Some other content</p>
          <img src="bar.gif" alt="" />
          <img src="blah.gif" alt="Blah!" />
    </body>
</html>

html可能非常庞大并且DOM严重嵌套,因此使用类似Html Agility Pack的东西就出来了。

有人能建议一种有效的方法来实现这一目标吗?

更新

一个安全的假设是我正在处理的html格式正确,因此潜在的解决方案根本不需要考虑到这一点。

3 个答案:

答案 0 :(得分:3)

您的问题看起来非常具体,您需要更改一些输出,但出于性能原因,您不希望解析整个事情(一般用途)HTMLAgilityPack。最好的解决方案似乎是艰难地做到这一点。

我会蛮力强迫它。要比这样的东西更有效率(很难完全未经测试,几乎保证不能完全正常工作,但逻辑应该没问题,如果在某处错过了“+1”或“-1”):

string addAltTag(string html) {
    StringBuilder sb = new StringBuilder();
    int pos=0;
    int lastPos=0;
    while(pos>=0) {
       int nextpos;
       pos=html.IndexOf("<img",pos);
       if (pos>=0) {
          // images can't have children, and there should not be any angle braces 
          // anyhere in the attributes, so should work fine
          nextPos =html.IndexOf(">",pos);

       }

       if (nextPos>0) {
          // back up if XML formed
          if (html.indexOf(nextPos-1,1)=="/") {
            nextPos--;
          }
           // output everything from last position up to but
           // before the closing caret
           sb.Append(html.Substring(lastPos,nextPos-lastPos-1);
           // can't just look for "alt" could be in the image url or class name
           if (html.Substring(pos,nextPos-pos).IndexOf(" alt=\"")<0) {
               sb.Append(" alt="\"\"");
           }
           lastPos=nextPos;
       } else {
           // unclosed image -- just quit
           pos=-1;
       }
    }
    sb.Append(html.Substring(lastPos);
    return sb.ToString();
}

您可能需要在测试之前转换为小写,解析或测试变体,例如alt = "(即带空格)等,具体取决于您对HTML的一致性。

顺便说一下,这种方法不会更快,但如果你想出于某种原因想要使用一些更通用的东西,你也可以给CsQuery一个镜头。这是我自己的jQuery的C#实现,可以很容易地做到这一点,例如。

obj.Select("img").Not("[alt]").Attr("alt",String.Empty);

由于你说HTML敏捷包在深度嵌套的HTML上表现不佳,这可能对你更好,因为我使用的HTML解析器不是递归的,并且无论嵌套如何都应该线性执行。但它会比编码确切需要慢得多,因为它确实将整个文档解析为对象模型。这对你的情况来说是否足够快,谁知道呢。

答案 1 :(得分:2)

我刚刚在8mb HTML文件上测试了这个,大约有250,000行。文档加载确实需要几秒钟,但选择方法非常快。不确定您的文件有多大或者您期望的是什么。我甚至编辑了HTML文件以包含一些缺失的标记,例如</body>和一些随机</div>。它仍然能够正确解析。

HtmlDocument doc = new HtmlDocument();
doc.Load(@"c:\\test.html");
HtmlNodeCollection col = doc.DocumentNode.SelectNodes("//img[not(@alt)]");

我总共有54,322个节点。选择花了几毫秒。

如果以上操作不起作用,并且您可以可靠地预测输出,则可以将文件流式传输并将其分解为可管理的块。

pseduo代码

  • 中的流文件
  • 解析HtmlAgilityPack
  • 循环直到流结束

我想你也可以在其中加入Parallel.ForEach(),虽然我找不到HtmlAgilityPack是否安全的文档。

答案 2 :(得分:2)

好吧,如果我检查您的内容是否符合Section 508,我将失败您的网站或内容 - 除非空白替代文字仅用于装饰(不需要理解内容)。< / p>

空白替代文字仅用于装饰。插入它可能会欺骗一些自动报告工具,但您肯定不符合Section 508合规性。

从项目管理的角度来看,最好不要让它失败,以便创建内容的最终用户负责,并且自动化工具会准确地将其报告为不合规。