来源:https://blog.csdn.net/tom0915/article/details/79037028

一、简单标签(SimpleTag)

  由于传统标签使用三个标签接口来完成不同的功能,显得过于繁琐,不利于标签技术的推广, SUN公司为降低标签技术的学习难度,在JSP 2.0中定义了一个更为简单、便于编写和调用的SimpleTag接口来实现标签的功能。

  

实现SimpleTag接口的标签通常称为简单标签。简单标签共定义了5个方法:

  • setJspContext方法
  • setParent和getParent方法
  • setJspBody方法
  • doTag方法(非常重要),简单标签使用这个方法就可以完成所有的业务逻辑

二、SimpleTag方法介绍

2.1、setJspContext方法

  用于把JSP页面的pageContext对象传递给标签处理器对象

2.2、setParent方法

  用于把父标签处理器对象传递给当前标签处理器对象

2.3、getParent方法

用于获得当前标签的父标签处理器对象

2.4、setJspBody方法

  用于把代表标签体的JspFragment对象传递给标签处理器对象

2.5、doTag方法

  用于完成所有的标签逻辑,包括输出、迭代、修改标签体内容等。在doTag方法中可以抛出javax.servlet.jsp.SkipPageException异常,用于通知WEB容器不再执行JSP页面中位于结束标记后面的内容,这等效于在传统标签的doEndTag方法中返回Tag.SKIP_PAGE常量的情况。

三、SimpleTag接口方法的执行顺序

  当web容器开始执行标签时,会调用如下方法完成标签的初始化:

  1. WEB容器调用标签处理器对象的setJspContext方法,将代表JSP页面的pageContext对象传递给标签处理器对象。
  2. WEB容器调用标签处理器对象的setParent方法,将父标签处理器对象传递给这个标签处理器对象。注意,只有在标签存在父标签的情况下,WEB容器才会调用这个方法。
  3. 如果调用标签时设置了属性,容器将调用每个属性对应的setter方法把属性值传递给标签处理器对象。如果标签的属性值是EL表达式或脚本表达式,则WEB容器首先计算表达式的值,然后把值传递给标签处理器对象。
  4. 如果简单标签有标签体,WEB容器将调用setJspBody方法把代表标签体的JspFragment对象传递进来。
  5. 执行标签时WEB容器调用标签处理器的doTag()方法,开发人员在方法体内通过操作JspFragment对象,就可以实现是否执行、迭代、修改标签体的目的。

四、开发简单标签实现页面逻辑

  SUN公司针对SimpleTag接口提供了一个默认的实现类SimpleTagSupport,SimpleTagSupport类中实现了SimpleTag接口的所有方法,因此我们可以编写一个类继承SimpleTagSupport类,然后根据业务需要再重写doTag方法。

4.1、控制jsp页面某一部分内容是否执行

 编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面不调用jspFrament.invoke方法即可。

示例代码如下:

SimpleTagDemo1.java

 1 package me.gacl.web.simpletag;2 3 import java.io.IOException;4 5 import javax.servlet.jsp.JspException;6 import javax.servlet.jsp.PageContext;7 import javax.servlet.jsp.tagext.JspFragment;8 import javax.servlet.jsp.tagext.SimpleTagSupport;9
10 /**
11  * @author gacl
12  * SimpleTagSupport类实现了SimpleTag接口,
13  * SampleTagDemo1类继承SimpleTagSupport
14  */
15 public class SimpleTagDemo1 extends SimpleTagSupport {
16
17     /* 简单标签使用这个方法就可以完成所有的业务逻辑
18      * @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
19      * 重写doTag方法,控制标签体是否执行
20      */
21     @Override
22     public void doTag() throws JspException, IOException {
23         //得到代表jsp标签体的JspFragment
24         JspFragment jspFragment = this.getJspBody();
25
26         //得到jsp页面的的PageContext对象
27         //PageContext pageContext = (PageContext) jspFragment.getJspContext();
28         //调用JspWriter将标签体的内容输出到浏览器
29         //jspFragment.invoke(pageContext.getOut());
30
31         //将标签体的内容输出到浏览器
32         jspFragment.invoke(null);
33
34     }
35 }

  在WEB-INF目录下新建一个simpletag.tld文件,然后在simpletag.tld文件中添加对该标签处理类的描述,如下:

  

  simpletag.tld文件代码如下:

 1 <?xml version="1.0" encoding="UTF-8" ?>2 3 <taglib xmlns="http://java.sun.com/xml/ns/j2ee"4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"5     xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"6     version="2.0">7     <!-- description用来添加对taglib(标签库)的描述 -->8     <description>孤傲苍狼开发的SimpleTag自定义标签库</description>9     <!--taglib(标签库)的版本号 -->
10     <tlib-version>1.0</tlib-version>
11     <short-name>GaclSimpleTagLibrary</short-name>
12     <!--
13         为自定义标签库设置一个uri,uri以/开头,/后面的内容随便写,如这里的/simpletag ,
14         在Jsp页面中引用标签库时,需要通过uri找到标签库
15         在Jsp页面中就要这样引入标签库:<%@taglib uri="/simpletag" prefix="gacl"%>
16     -->
17     <uri>/simpletag</uri>
18
19     <!--一个taglib(标签库)中包含多个自定义标签,每一个自定义标签使用一个tag标记来描述  -->
20     <!-- 一个tag标记对应一个自定义标签 -->
21      <tag>
22         <description>SimpleTag(简单标签)Demo1</description>
23         <!--
24             为标签处理器类配一个标签名,在Jsp页面中使用标签时是通过标签名来找到要调用的标签处理器类的
25             通过demo1就能找到对应的me.gacl.web.simpletag.SimpleTagDemo1类
26          -->
27         <name>demo1</name>
28         <!-- 标签对应的处理器类-->
29         <tag-class>me.gacl.web.simpletag.SimpleTagDemo1</tag-class>
30         <!--
31         tld文件中有四种标签体类型 :empty  JSP  scriptless  tagdepentend
32             在简单标签(SampleTag)中标签体body-content的值只允许是empty和scriptless,不允许设置成JSP,如果设置成JSP就会出现异常
33             在传统标签中标签体body-content的值只允许是empty和JSP
34             如果标签体body-content的值设置成tagdepentend,那么就表示标签体里面的内容是给标签处理器类使用的,
35             例如:开发一个查询用户的sql标签,此时标签体重的SQL语句就是给SQL标签的标签处理器来使用的
36             <gacl:sql>SELECT * FROM USER</gacl:sql>
37             在这种情况下,sql标签的<body-content>就要设置成tagdepentend,tagdepentend用得比较少,了解一下即可
38         -->
39         <body-content>scriptless</body-content>
40     </tag>
41 </taglib>

  在jsp页面中导入并使用自定义标签,如下:

 1 <%@ page language="java" pageEncoding="UTF-8"%>2 <%--在jsp页面中导入自定义标签库 --%>3 <%@taglib uri="/simpletag" prefix="gacl" %>4 <!DOCTYPE HTML>5 <html>6   <head>7     <title>用简单标签控制标签体是否执行</title>8   </head>9
10   <body>
11
12   <%--在jsp页面中使用自定义标签 demo1标签是带有标签体的,标签体的内容是"孤傲苍狼"这几个字符串--%>
13     <gacl:demo1>
14         孤傲苍狼
15     </gacl:demo1>
16   </body>
17 </html>

  运行效果如下:

  

4.2、控制jsp页面内容重复执行

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法里面重复调用jspFrament.invoke方法即可。

示例代码如下:

SimpleTagDemo2.java

 1 package me.gacl.web.simpletag;2 3 import java.io.IOException;4 5 import javax.servlet.jsp.JspException;6 import javax.servlet.jsp.tagext.JspFragment;7 import javax.servlet.jsp.tagext.SimpleTagSupport;8 9 /**
10  * @author gacl
11  * SimpleTagSupport类实现了SimpleTag接口,
12  * SampleTagDemo2类继承SimpleTagSupport
13  */
14 public class SimpleTagDemo2 extends SimpleTagSupport {
15
16     /* 简单标签使用这个方法就可以完成所有的业务逻辑
17      * @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
18      * 重写doTag方法,控制标签执行5次
19      */
20     @Override
21     public void doTag() throws JspException, IOException {
22         // 得到代表jsp标签体的JspFragment
23         JspFragment jspFragment = this.getJspBody();
24         for (int i = 0; i < 5; i++) {
25             // 将标签体的内容输出到浏览器
26             jspFragment.invoke(null);
27         }
28     }
29 }

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

1 <tag>
2         <!-- 标签名 -->
3         <name>demo2</name>
4         <!-- 标签处理器类-->
5         <tag-class>me.gacl.web.simpletag.SimpleTagDemo2</tag-class>
6         <!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
7         <body-content>scriptless</body-content>
8 </tag>

  在jsp页面中导入并使用自定义标签,如下:

 1 <%@ page language="java" pageEncoding="UTF-8"%>2 <%--在jsp页面中导入自定义标签库 --%>3 <%@taglib uri="/simpletag" prefix="gacl" %>4 <!DOCTYPE HTML>5 <html>6   <head>7     <title>用简单标签控制标签体执行5次</title>8   </head>9
10   <body>
11
12   <%--在jsp页面中使用自定义标签 --%>
13     <gacl:demo2>
14         孤傲苍狼<p/>
15     </gacl:demo2>
16   </body>
17 </html>

  运行效果如下:

  

4.3、修改jsp页面内容输出

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法调用jspFrament.invoke方法时,让执行结果写一个自定义的缓冲中即可,然后开发人员可以取出缓冲的数据修改输出。

示例代码如下:

SimpleTagDemo3.java

 1 package me.gacl.web.simpletag;2 3 import java.io.IOException;4 import java.io.StringWriter;5 6 import javax.servlet.jsp.JspException;7 import javax.servlet.jsp.PageContext;8 import javax.servlet.jsp.tagext.JspFragment;9 import javax.servlet.jsp.tagext.SimpleTagSupport;
10
11 /**
12  * @author gacl
13  * SimpleTagSupport类实现了SimpleTag接口,
14  * SampleTagDemo3类继承SimpleTagSupport
15  */
16 public class SimpleTagDemo3 extends SimpleTagSupport {
17
18     /* 简单标签使用这个方法就可以完成所有的业务逻辑
19      * @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
20      * 重写doTag方法,修改标签体里面的内容,将标签体的内容转换成大写
21      */
22     @Override
23     public void doTag() throws JspException, IOException {
24         // 得到代表jsp标签体的JspFragment
25         JspFragment jspFragment = this.getJspBody();
26         StringWriter sw = new StringWriter();
27         //将标签体的内容写入到sw流中
28         jspFragment.invoke(sw);
29         //获取sw流缓冲区的内容
30         String content = sw.getBuffer().toString();
31         content = content.toUpperCase();
32         PageContext pageContext = (PageContext) this.getJspContext();
33         //将修改后的content输出到浏览器中
34         pageContext.getOut().write(content);
35     }
36 }

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

1 <tag>
2         <!-- 标签名 -->
3         <name>demo3</name>
4         <!-- 标签处理器类-->
5         <tag-class>me.gacl.web.simpletag.SimpleTagDemo3</tag-class>
6         <!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
7         <body-content>scriptless</body-content>
8 </tag>

  在jsp页面中导入并使用自定义标签,如下:

 1 <%@ page language="java" pageEncoding="UTF-8"%>2 <%--在jsp页面中导入自定义标签库 --%>3 <%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>4 <%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>5 <%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>6 <!DOCTYPE HTML>7 <html>8   <head>9     <title>用简单标签修改jsp页面内容输出</title>
10   </head>
11
12   <body>
13
14   <%--在jsp页面中使用自定义标签 --%>
15     <gacl:demo3>
16         gacl_xdp
17     </gacl:demo3>
18   </body>
19 </html>

  运行效果如下:

  

4.4、控制整个jsp页面是否执行

  编写一个类继承SimpleTagSupport,然后再重写doTag方法,在doTag方法抛出SkipPageException异常即可,jsp收到这个异常,将忽略标签余下jsp页面的执行。

示例代码如下:

SimpleTagDemo4.java

 1 package me.gacl.web.simpletag;2 3 import java.io.IOException;4 import javax.servlet.jsp.JspException;5 import javax.servlet.jsp.SkipPageException;6 import javax.servlet.jsp.tagext.SimpleTagSupport;7 8 /**9  * @author gacl
10  * SimpleTagSupport类实现了SimpleTag接口,
11  * SampleTagDemo4类继承SimpleTagSupport
12  */
13 public class SimpleTagDemo4 extends SimpleTagSupport {
14
15     /* 简单标签使用这个方法就可以完成所有的业务逻辑
16      * @see javax.servlet.jsp.tagext.SimpleTagSupport#doTag()
17      * 重写doTag方法,控制标签余下的Jsp不执行
18      */
19     @Override
20     public void doTag() throws JspException, IOException {
21         //抛出一个SkipPageException异常就可以控制标签余下的Jsp不执行
22         throw new SkipPageException();
23     }
24 }

  在WEB-INF目录下的simpletag.tld文件中添加对该标签处理类的描述,如下:

1 <tag>
2         <!-- 标签名 -->
3         <name>demo4</name>
4         <!-- 标签处理器类-->
5         <tag-class>me.gacl.web.simpletag.SimpleTagDemo4</tag-class>
6         <!-- 标签体允许的内容 ,empty表示该标签没有标签体-->
7         <body-content>empty</body-content>
8 </tag>

  在jsp页面中导入并使用自定义标签,如下:

 1 <%@ page language="java" pageEncoding="UTF-8"%>2 <%--在jsp页面中导入自定义标签库 --%>3 <%--<%@taglib uri="/simpletag" prefix="gacl" %>--%>4 <%--在jsp页面中也可以使用这种方式导入标签库,直接把uri设置成标签库的tld文件所在目录 --%>5 <%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>6 <!DOCTYPE HTML>7 <html>8   <head>9     <title>用简单标签控制标签余下的Jsp不执行</title>
10   </head>
11
12   <body>
13       <h1>孤傲苍狼</h1>
14        <%--在jsp页面中使用自定义标签 --%>
15        <gacl:demo4/>
16        <!-- 这里的内容位于 <gacl:demo4/>标签后面,因此不会输出到页面上 -->
17        <h1>白虎神皇</h1>
18   </body>
19 </html>

  运行效果如下:

  

五、简单标签开发的一些注意细节

5.1、标签类编写细节

  开发标签类时,不要直接去实现SimpleTag接口,而是应该继承SimpleTagSupport类,SimpleTagSupport类是SimpleTag接口的一个默认实现类,通过继承SimpleTagSupport类,就可以直接使用SimpleTagSupport类已经实现的那些方法,如果SimpleTagSupport类的方法实现不满足业务要求,那么就可以根据具体的业务情况将相应的方法进行重写。

5.2、tld文件中标签体类型设置细节

  我们开发好一个简单标签后,需要在tld文件中添加对该标签的描述,例如:

1 <tag>
2         <!-- 标签名 -->
3         <name>demo2</name>
4         <!-- 标签处理器类-->
5         <tag-class>me.gacl.web.simpletag.SimpleTagDemo2</tag-class>
6         <!-- 标签体允许的内容 ,scriptless表示标签体的内容不允许是java脚本代码-->
7         <body-content>scriptless</body-content>
8 </tag>

  开发好一个标签后,在tld文件中使用<tag>来描述一个标签,描述的内容包括标签名(name),标签处理器类(tag-class),标签体的内容(body-content)。

  tld文件中有四种标签体(body-content)类型 :empty、JSP、scriptless、tagdependent

简单标签标签体的细节注意问题:
     在简单标签(SampleTag)中标签体body-content的值只允许是empty、scriptless、tagdependent,不允许设置成JSP,如果设置成JSP就会出现异常

1 The TLD for the class me.gacl.web.simpletag.SimpleTagDemo1 specifies an invalid body-content (JSP) for a SimpleTag

  body-content的值如果设置成empty,那么就表示该标签没有标签体,如果是设置成scriptless,那么表示该标签是有标签体的,但是标签体中的内容不可以是<%java代码%>,例如:

1 <gacl:xdpdemo1>
2    <%
3            //嵌套在标签体中的java代码
4         int i= 0;
5     %>
6       孤傲苍狼
7 </gacl:xdpdemo1>

  否则运行标签时就会出现如下错误:

1 Scripting elements ( &lt;%!, &lt;jsp:declaration, &lt;%=, &lt;jsp:expression, &lt;%, &lt;jsp:scriptlet ) are disallowed here

  jsp标签技术出现的目的就是为了移除在jsp页面上编写的java代码的,如果在jsp标签中允许出现java代码,那么就违背了jsp标签技术设计时的初衷了。所以在简单标签的标签体中是不允许出现java代码的。

传统标签标签体的细节注意问题:
     在传统标签中标签体body-content的值允许是empty、JSP、scriptless、tagdependentbody-content的值如果是设置成JSP那么表示该标签是有标签体的,并且标签体的内容可以是任意的,包括java代码,如果是设置成scriptless那么表示该标签是有标签体的,但是标签体的内容不能是java代码

  如果传统标签简单标签的标签体body-content的值设置成tagdependent,那么就表示标签体里面的内容是给标签处理器类使用的,tagdependent用得比较少,了解一下即可

5.3、tld文件中标签库的uri设置细节

  如果在一个项目中使用或者开发了多个标签库,例如:

  

  那么标签库的uri不能设置成相同的,否则在Jsp页面中通过uri引用标签库时就不知道引用哪一个标签库了,如果真的有那么巧,两个标签库的uri是刚好一样的,如下图所示:

  

  那么在jsp页面中引用标签库时如果"<%@taglib uri="/gacl" prefix="gacl" %>"这样引用,那么就无法判断当前引用的标签库到底是gacl.tld标签库中的标签还是simpletag.tld标签库中的标签,因为两个标签库的uri刚好都是"/gacl",在两个标签库的引用uri一样的情况下,为了能够在jsp中区别到底引用的是哪个标签库,可以换一种引用方式:<%@taglib uri="要引用的标签库的tld文件目录" prefix="gacl"%>,使用taglib指令引入标签库时,taglib指令的uri属性指定为标签库的tld文件目录,这样就可以区别开了,例如:

  引用gacl.tld标签库:<%@taglib uri="/WEB-INF/gacl.tld" prefix="gacl"%>、

  引用simpletag.tld标签库:<%@taglib uri="/WEB-INF/simpletag.tld" prefix="gacl"%>

  所以当在项目中引用了多个标签库,如果标签库的uri刚好是一样的,就可以用这种方式解决。

六、简单标签开发步骤总结

  1、编写一个类继承SimpleTagSupport类,然后根据业务需要重写SimpleTagSupport类中已经实现了的方法,一般情况下只需要重写doTag()方法即可。

  2、在WEB-INF目录下创建一个tld文件,在tld文件中添加对该标签的描述。tld文件不一定放在WEB-INF目录下,也可以放在别的目录,习惯是放在WEB-INF目录下。

javawen学习自定义标签开发相关推荐

  1. 转载:javaweb学习总结(二十三)——jsp自定义标签开发入门

    javaweb学习总结(二十三)--jsp自定义标签开发入门 转自:http://www.cnblogs.com/xdp-gacl/p/3916734.html 一.自定义标签的作用 自定义标签主要用 ...

  2. javaweb学习总结(二十三)——jsp自定义标签开发入门

    一.自定义标签的作用 自定义标签主要用于移除Jsp页面中的java代码. 二.自定义标签开发和使用 2.1.自定义标签开发步骤 1.编写一个实现Tag接口的Java类(标签处理器类) 1 packag ...

  3. javaweb学习总结(二十三):jsp自定义标签开发入门

    一.自定义标签的作用 自定义标签主要用于移除Jsp页面中的java代码. 二.自定义标签开发和使用 2.1.自定义标签开发步骤 1.编写一个实现Tag接口的Java类(标签处理器类) 1 packag ...

  4. JSP自定义标签开发与打包

    前言:最近用到JSP的自定义标签,于是到网上寻找资料,结果发现很多的网上教程要么不全要么就是步骤流程错误,尤其是将自定义标签打jar包这一过程,按照网上的教程操作,结果被坑惨了,所以才有现在这一篇博文 ...

  5. JSP自定义标签开发入门

    一般情况下开发jsp自定义标签需要引用以下两个包 import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; 首先我们需要大致了解开发 ...

  6. 【自定义标签开发】01-标签简介和开发第一个标签

    自定义标签简介 自定义标签主要用于移除Jsp页面中的java代码. 要使用自定义标签移除jsp页面中的java代码,只需要完成以下两个步骤: 1.编写一个实现Tag接口的java类,把页面java代码 ...

  7. JSP自定义标签开发(五)——标签类获取 request 、 session

    标签类中要是能够取到 request 和 session 这样的对象,对于一个web开发员来说,那是一件多么幸福的事情啊!懂得了这个知识,标签开发与servlet结合的开发将变得随心所欲. 实例: p ...

  8. java web 自定义标签_JavaWeb学习——自定义标签

    自定义标签 一.自定义标签概述 使用标准JSP访问.操作JavaBean,是实现展现(HTML)与业务实现(Java代码)分离的第一步.然而,标准方法功能不够强大,以至于开发者无法仅仅使用它们开发应用 ...

  9. pbootcms自定义标签开发_替换标题中的特殊符号

    如题:最新在使用pbootcms进行建站,现有的标签不符合自己的一些业务需求,想着自己做个符合自己的业务的标签,于是参考了一下网上的资料以后便开始着手开发了,整个二开还是比较简单,下面放出来需要修改的 ...

最新文章

  1. AJAX ControlToolkit学习日志-ModalPopupExtender(16)
  2. Oracle 11gR2构建RAC之(3)--安装grid前环境检测
  3. 【转】北京四中学习方法
  4. elecV2P实现任务定时通知
  5. prefixspan是挖掘频繁子序列,子序列不一定是连续的,当心!!!
  6. 从宏观经济状况看目前股市
  7. windows下qt5 kinect 2.0开发与环境配置
  8. Framework学习(二)Android的启动过程
  9. Rational AppScan 标准版可扩展性和二次开发能力简介
  10. CQOI 2016 不同的最小割
  11. 中国人民公安大学(PPSUC) 网络对抗技术作业一
  12. win10系统dnf安装不上服务器,升级到Win10正式版后不能玩DNF如何解决?
  13. 不应被忘记的历史——聊聊前阿布时代的切尔西
  14. Ubuntu 10.04 HP LaserJet 无法打印 **** Unable to open the initial device, quitting 错误
  15. 计算机故障四类,计算机内存出现问题的四大症状
  16. SpringBoot项目的云服务器部署
  17. 某公司公众号任意用户注册漏洞利用
  18. Messaging短信源码导入AndroidStudio
  19. js随机字符a-zA-Z0-9
  20. shell之read用法

热门文章

  1. 小米手机图标消失问题
  2. 企业为什么要申报高新技术企业
  3. 深聊全链路压测之:第二十三讲 | 如何改造性能监控。
  4. 洛谷——P1017 [NOIP2000 提高组] 进制转换(C++)
  5. 基于Matlab——夫琅禾夫衍射以及菲涅尔衍射
  6. 打印机驱动冲突和端口异常:win10更新部分补丁后,打印机本地连接(连接打印机的主机)可以打印,其他共享网络中的电脑可以连接到打印机,但不能打印——解决方案
  7. cubemx 使用 学习跳转链接
  8. TI 280049 FLASH 翻译
  9. 写给初入行,刚接触Auto CAD的新人一些建议——第一期
  10. Linux学习5之查找文件中的某个字符串并返回所在行号