C#递归方法出乎意料的结果

时间:2009-07-14 04:03:35

标签: c# html string text recursion

我有一个相当简单的方法,递归删除开始/结束的html标签

class Program
    {   
        static void Main(string[] args)
        {
            string s = FixHtml("<div><p>this is a <strong>test</strong></p></div>");
            Console.WriteLine(s);
        }

        private static string FixHtml(string s)
        {            
            //Remove any outer <div>
            if (s.ToLower().StartsWith("<div>"))
            {
                FixHtml(s.Substring(5, s.Length - 5));
            }
            else if (s.ToLower().StartsWith("<p>"))
            {
                FixHtml(s.Substring(3, s.Length - 3));
            }
            else if (s.ToLower().EndsWith("</div>"))
            {
                FixHtml(s.Substring(0, s.Length - 6));
            }
            else if (s.ToLower().EndsWith("</p>"))
            {
                FixHtml(s.Substring(0, s.Length - 4));
            }

            return s;
        }
    }

行为是它可以递归删除<div> & <p>标签,但是在“return s”语句中它撤消所有工作,通过添加回添加标签!

任何人都知道为什么会这样吗?以及如何强制它返回我想要的值。即this is a <strong>test</strong>

6 个答案:

答案 0 :(得分:14)

在.NET中,字符串是不可变的 - 因此您的方法实际上永远不会更改返回值。当您致电s.ToLower().StartsWith("<div>")时,您会收到带有预期差异的新字符串现有字符串s 保持不变。

此外,您永远不会使用递归调用的返回值。

离开我的头顶,尝试这样的事情:

    private static string FixHtml(string s)
    {            
        if (s.ToLower().StartsWith("<div>"))
        {
            return FixHtml(s.Substring(5, s.Length - 5));
        }
        else if (s.ToLower().StartsWith("<p>"))
        {
            return FixHtml(s.Substring(3, s.Length - 3));
        }
        else if (s.ToLower().EndsWith("</div>"))
        {
            return FixHtml(s.Substring(0, s.Length - 6));
        }
        else if (s.ToLower().EndsWith("</p>"))
        {
            return FixHtml(s.Substring(0, s.Length - 4));
        }

        return s;
    }

答案 1 :(得分:5)

请注意,原始文本操作通常是处理xml的一种不好的方法 - 例如,您现在不处理属性,名称空间,尾随标记空白(<p >)等。

通常,我会说把它加载到DOM(XmlDocument / XDocument用于xhtml; HTML Agaility Pack用于html) - 但实际上我想知道在这种情况下xslt是否会好。 ..

例如:

static void Main()
{
    string xhtml = @"<div><p>this is a <strong>test</strong></p></div>";
    XslCompiledTransform xslt = new XslCompiledTransform();
    xslt.Load("strip.xslt");

    StringWriter sw = new StringWriter();
    using(XmlReader xr = XmlReader.Create(new StringReader(xhtml))) {
        xslt.Transform(xr, null, sw);
    }
    string newHtml = sw.ToString();
    Console.WriteLine(newHtml);
}

使用strip.xslt:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="no" omit-xml-declaration="yes"/>
  <xsl:template match="strong|@*">
    <xsl:copy><xsl:apply-templates select="*|text()"/></xsl:copy>
  </xsl:template>
  <xsl:template match="*">
    <xsl:apply-templates select="*|text()"/>
  </xsl:template>
</xsl:stylesheet>

答案 2 :(得分:3)

  • 你没有对嵌套调用返回的字符串做任何事情。
  • 当字符串被更改时,会创建一个新对象,而不是现有的对象被更改(它们是不可变的)。
  • 如果您想在不使用返回值的情况下采用类似方法,则可以将字符串参数设为'ref'参数。虽然其他人提到的性能下降仍然适用。

答案 3 :(得分:2)

您需要为每个FixHtml调用添加一个返回值,如下所示:

   private static string FixHtml(string s)
    {            
        //Remove any outer <div>
        if (s.ToLower().StartsWith("<div>"))
        {
            return FixHtml(s.Substring(5, s.Length - 5));
        }
        else if (s.ToLower().StartsWith("<p>"))
        {
            return FixHtml(s.Substring(3, s.Length - 3));
        }
        else if (s.ToLower().EndsWith("</div>"))
        {
            return FixHtml(s.Substring(0, s.Length - 6));
        }
        else if (s.ToLower().EndsWith("</p>"))
        {
            return FixHtml(s.Substring(0, s.Length - 4));
        }

        return s;
    }

答案 4 :(得分:2)

您需要使用StringBuilder来实现此功能,或者在FixHTML的每次调用中复制字符串以使其生效。这是因为字符串在.NET中是不可变的。

您可以look here查看不可变字符串是什么。

答案 5 :(得分:2)

如果您计划在服务器上执行此操作,则必须使用字符串构建器。原因是如果您使用字符串,内存性能将是HORRENDOUS。实际上,每次从字符串中剥离标记时,都可以有效地复制字符串。对于每个递归(标记),您的系统将执行此操作,因此,如果您甚至有合理大小的HTML输入,您将很快使用大量内存。

编辑:参考Chris的评论,如果你正在处理大字符串,那么前面的陈述是正确的。如果您使用字符串构建器解析小块HTML并不重要。但我假设您在Web环境中的服务器上使用它,因此您可能会使用它来占用非常大的页面。

使用字符串构建器作为引用也将允许您的函数操作可变值,因此在递归结束时,StringBuilder.ToString()将正确输出您的变异字符串。

如果您提出我的解决方案,请将其他提到字符串可变性的人作为您的问题,请::)。

我试图回答你的问题,然后解决下一个问题,认为我错误很多。

另请注意,您的代码会因<br/>

而死亡
private static string FixHtml(StringBuilder bldr)    
{                    
    if (String.Compare(blder.ToString(0,5), "<div>", true) == 0)        
    {
        blder.remove(0, 5);            
        return FixHtml(blder);        
    }       
    else if (String.Compare(blder.ToString(0,3), "<p>", true) == 0)       
    {
        blder.remove(0, 3);            
        return FixHtml(blder);             
    }        
    else if (String.Compare(blder.ToString(bldr.Length - 6, 6), "</div>", true) == 0)       
    {
        blder.remove(blder.Length - 6, 6);            
        return FixHtml(blder);                   
    }        
    else if (String.Compare(blder.ToString(bldr.Length - 4, 4), "</p>", true) == 0)       
    {        
        blder.remove(blder.Length - 4, 4);            
        return FixHtml(blder); 
    }       
    return blder.ToString();    
}