R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)

包含以下几个小的知识点
1htmlTreeParse函数源码和一些参数
2hander的写法
3关于missing函数
4关于if-else语句中else语句的花括号问题
5关于checkHandlerNames函数
6关于GeneralHandlerNames属性
7关于match函数
8关于inherits函数
9关于on.exit函数
===============================================================================
还是尝试去阅读下该函数的源码:
htmlTreeParse函数
  1. function (file, ignoreBlanks = TRUE, handlers = NULL, replaceEntities = FALSE,
  2. asText = FALSE, trim = TRUE, validate = FALSE, getDTD = TRUE,
  3. isURL = FALSE, asTree = FALSE, addAttributeNamespaces = FALSE,
  4. useInternalNodes = FALSE, isSchema = FALSE, fullNamespaceInfo = FALSE,
  5. encoding = character(), useDotNames = length(grep("^\\.",
  6. names(handlers)))>0, xinclude = TRUE, addFinalizer = TRUE,
  7. error = htmlErrorHandler, isHTML = TRUE, options = integer(),
  8. parentFirst = FALSE)
  9. {
  10. #asText=T则file作为XML文本处理
  11. isMissingAsText = missing(asText)
  12. #地址大于一个时要中止程序并抛出异常
  13. if(length(file)>1){
  14. file = paste(file, collapse ="\n")
  15. if(!missing(asText)&&!asText)
  16. stop(structure(list(message ="multiple URLs passed to xmlTreeParse. If this is the content of the file, specify asText = TRUE"),
  17. class= c("MultipleURLError","XMLParserError",
  18. "simpleError","error","condition")))
  19. asText = TRUE
  20. }
  21. #当isURL非空时且是XML的时候,才修改URL为数值型
  22. #比如这里isURL=1
  23. if(missing(isURL)&&!asText)
  24. isURL <- length(grep("^(http|ftp|file)://", file, useBytes = TRUE,
  25. perl = TRUE))
  26. #isHTML 默认为 TRUE
  27. if(isHTML){
  28. validate = FALSE
  29. getDTD = FALSE
  30. isSchema = FALSE
  31. docClass ="HTMLInternalDocument"
  32. }
  33. else docClass = character()
  34. #checkHandlerNames返回的是一个逻辑值,其作用是
  35. checkHandlerNames(handlers,"DOM")
  36. if(missing(fullNamespaceInfo)&& inherits(handlers,"RequiresNamespaceInfo"))
  37. fullNamespaceInfo = TRUE
  38. oldValidate = xmlValidity()
  39. xmlValidity(validate)
  40. on.exit(xmlValidity(oldValidate))
  41. if(!asText && isURL == FALSE){
  42. if(file.exists(file)== FALSE)
  43. if(!missing(asText)&& asText == FALSE){
  44. e = simpleError(paste("File", file,"does not exist"))
  45. class(e)= c("FileNotFound",class(e))
  46. stop(e)
  47. }
  48. else asText <- TRUE
  49. }
  50. if(asText && length(file)>1)
  51. file = paste(file, collapse ="\n")
  52. old = setEntitySubstitution(replaceEntities)
  53. on.exit(setEntitySubstitution(old), add = TRUE)
  54. if(asText && length(grep(sprintf("^%s?\\s*<",BOMRegExp),
  55. file, perl = TRUE, useBytes = TRUE))==0){
  56. if(!isHTML ||(isMissingAsText &&!inherits(file,"AsIs"))){
  57. e = simpleError(paste("XML content does not seem to be XML:",
  58. sQuote(file)))
  59. class(e)= c("XMLInputError",class(e))
  60. (if(isHTML)
  61. warning
  62. else stop)(e)
  63. }
  64. }
  65. if(!is.logical(xinclude)){
  66. xinclude =as.logical(xinclude)
  67. }
  68. if(!asText &&!isURL)
  69. file = path.expand(as.character(file))
  70. if(useInternalNodes && trim){
  71. prevBlanks =.Call("RS_XML_setKeepBlanksDefault",0L,
  72. PACKAGE ="XML")
  73. on.exit(.Call("RS_XML_setKeepBlanksDefault", prevBlanks,
  74. PACKAGE ="XML"), add = TRUE)
  75. }
  76. .oldErrorHandler = setXMLErrorHandler(error)
  77. on.exit(.Call("RS_XML_setStructuredErrorHandler",.oldErrorHandler,
  78. PACKAGE ="XML"), add = TRUE)
  79. if(length(options))
  80. options = sum(options)
  81. ans <-.Call("RS_XML_ParseTree",as.character(file), handlers,
  82. as.logical(ignoreBlanks),as.logical(replaceEntities),
  83. as.logical(asText),as.logical(trim),as.logical(validate),
  84. as.logical(getDTD),as.logical(isURL),as.logical(addAttributeNamespaces),
  85. as.logical(useInternalNodes),as.logical(isHTML),as.logical(isSchema),
  86. as.logical(fullNamespaceInfo),as.character(encoding),
  87. as.logical(useDotNames), xinclude, error, addFinalizer,
  88. as.integer(options),as.logical(parentFirst), PACKAGE ="XML")
  89. if(!missing(handlers)&& length(handlers)&&!as.logical(asTree))
  90. return(handlers)
  91. if(!isSchema && length(class(ans)))
  92. class(ans)= c(docClass, oldClass(class(ans)))
  93. if(inherits(ans,"XMLInternalDocument"))
  94. addDocFinalizer(ans, addFinalizer)
  95. elseif(!getDTD &&!isSchema){
  96. class(ans)= oldClass("XMLDocumentContent")
  97. }
  98. ans
  99. }
  100. <environment: namespace:XML>
查看整个htmlTreeParse函数的源代码,可以用到handler的地方并不多。
 
 
我们看一下xmlTreeParse {XML}中对于该参数的描述:
参数handlers:

Optional collection of functions(函数集) used to map the different XML nodes to R objects. Typically, this is a named list of functions, and a closure(闭包) can be used to provide local data. This provides a way of filtering the tree as it is being created in R, adding or removing nodes, and generally processing them as they are constructed in the C code.
In a recent addition to the package (version 0.99-8), if this is specified as a single function object, we call that function for each node (of any type) in the underlying DOM tree. It is invoked with(调用) the new node(新节点) and its parent node(父节点). This applies to regular nodes(普通节点) and also comments(注释), processing instructions, CDATA nodes, etc. So this function must be sufficiently general to handle them all.

 
 
asTree参数

this only applies when on passes a value for the handlers argument and is used then to determine whether the DOM tree should be returned or the handlers object.

该参数在有传入处理器函数的时候要设置为TRUE,此时,函数的返回值是处理后的DOM树,而不是handlers函数对象本身
 
useDotNames参数

a logical value indicating whether to use the newer format for identifying general element function handlers with the '.' prefix, e.g. .text, .comment, .startElement. If this is FALSE, then the older format text, comment, startElement, ... are used. This causes problems when there are indeed nodes named text or comment or startElement as a node-specific handler are confused with the corresponding general handler of the same name(如果函数名叫startElement,而有个节点(自定义标签吧),也交startElement就会让函数困惑滴). Using TRUE means that your list of handlers should have names that use the '.' prefix for these general element handlers. This is the preferred way to write new code.

TRUE是使用新的格式(函数以.开头)来区分
FLALSE是用旧的格式
实际上配合参数里的这一句很容易理解:
useDotNames = length(grep("^\\.", names(handlers))) > 0
取得处理函数的名字,看看其是否以点开头,如果长度不大于0,就是没有,就是FALSE咯
 
 
isURL参数

indicates whether the file argument refers to a URL (accessible via ftp or http) or a regular file on the system. If asText is TRUE, this should not be specified. The function attempts to determine whether the data source is a URL by using grep to look for http or ftp at the start of the string. The libxml parser handles the connection to servers, not the R facilities (e.g. scan).

 
 
hander的写法——在文档的Details中:

The handlers argument is used similarly to those specified in xmlEventParse. When an XML tag (element) is processed, we look for a function in this collection with the same name as the tag's name. If this is not found, we look for one named startElement. If this is not found, we use the default built in converter(变换器). The same works for comments, entity references, cdata, processing instructions, etc. The default entries should be named comment, startElement, externalEntity, processingInstruction, text, cdata and namespace. All but the last should take the XMLnode as their first argument. In the future, other information may be passed via ..., for example, the depth in the tree, etc. Specifically, the second argument will be the parent node into which they are being added, but this is not currently implemented, so should have a default value (NULL).
当一个标签被处理时,在函数集里1先找和标签同名的函数,2找startElement,最后才找默认的函数
当一个注释等被处理时,也是一样。那么也就是说,比如处理注释的时候,先找叫comment的函数呗,
所以,handler中的函数命名(列表的组件名)是需要讲规律的
node必须作为这些函数的第一个参数。
嗯,这一段讲的很好~,把handler函数怎么编写讲清楚了。
 
这个博客,也提到了这些参数的含义,有空可以看看
 
=======================================================================
  1. #test
  2. #导包
  3. library(XML)
  4. #链接
  5. url <-"http://www.r-datacollection.com/materials/html/fortunes.html"
  6. #handers函数
  7. h2 <- list(
  8. startElement = function(node,...){
  9. name <- xmlName(node)
  10. if(name %in% c("div","title")){NULL}else{node}
  11. },
  12. comment = function(node){NULL}
  13. )
  14. #正式开始,函数参数
  15. file=url
  16. ignoreBlanks = TRUE
  17. handlers = h2
  18. replaceEntities = FALSE
  19. asText = FALSE
  20. trim = TRUE
  21. validate = FALSE
  22. getDTD = TRUE
  23. isURL = FALSE
  24. asTree = TRUE
  25. addAttributeNamespaces = FALSE
  26. useInternalNodes = FALSE
  27. isSchema = FALSE
  28. fullNamespaceInfo = FALSE
  29. encoding = character()
  30. useDotNames = length(grep("^\\.", names(handlers)))>0
  31. xinclude = TRUE
  32. addFinalizer = TRUE
  33. error = XML:::htmlErrorHandler
  34. isHTML = TRUE
  35. options = integer()
  36. parentFirst = FALSE
  37. #函数体部分
  38. #asText参数没有传入
  39. #我们将其从missing(asText)改为TRUE
  40. isMissingAsText = TRUE
  41. #file的长度是否大于1,大于1如果asText未传入则要报错:传入了多个URL
  42. #不大于1,则跳过
  43. if(length(file)>1){
  44. file = paste(file, collapse ="\n")
  45. if(!missing(asText)&&!asText)
  46. stop(structure(list(message ="multiple URLs passed to xmlTreeParse. If this is the content of the file, specify asText = TRUE"),
  47. class= c("MultipleURLError","XMLParserError",
  48. "simpleError","error","condition")))
  49. asText = TRUE
  50. }
  51. # 本来是if (missing(isURL) && !asText)
  52. # 我们修改为if (TURE && !asText)
  53. #isURL参数没有传递且asText参数为假才执行
  54. if(TRUE &&!asText)
  55. isURL <- length(grep("^(http|ftp|file)://", file, useBytes = TRUE,
  56. perl = TRUE))
  57. #只要有http|ftp|file中的一个协议开头,比如http://,就是URL了。
  58. # 此时isURL=1
  59. #是否为HTML,是的
  60. if(isHTML){
  61. validate = FALSE
  62. getDTD = FALSE #从默认值T改为了F
  63. isSchema = FALSE
  64. docClass ="HTMLInternalDocument"
  65. }else{docClass = character()}#否则创建空的
  66. # class(docClass)
  67. #返回TRUE通过检验了,否则函数会中止并抛出异常
  68. XML:::checkHandlerNames(handlers,"DOM")
  69. #fullNamespaceInfo参数whether to provide the namespace URI and prefix on each node
  70. #其实就是是否在节点面前带上URI信息
  71. #fullNamespaceInfo为空且handlers含有该属性,才执行这一步
  72. #missing(fullNamespaceInfo)被替换为TRUE
  73. #handlers并没有RequiresNamespaceInfo属性,所以不执行
  74. if( TRUE && inherits(handlers,"RequiresNamespaceInfo"))
  75. fullNamespaceInfo = TRUE
  76. #以下两行的结果都是integer(0)
  77. #奇妙~,先保存原有的配置
  78. oldValidate = XML:::xmlValidity()
  79. #当前的配置
  80. XML:::xmlValidity(validate)
  81. #还原原来的配置
  82. on.exit(XML:::xmlValidity(oldValidate))
  83. #asText为假,且isURL是假的
  84. #结果为TFALSE,我们不必管他
  85. if(!asText && isURL == FALSE){
  86. if(file.exists(file)== FALSE)
  87. #如果本地文件不存在
  88. if(!missing(asText)&& asText == FALSE){
  89. #抛出异常,文件不存在
  90. e = simpleError(paste("File", file,"does not exist"))
  91. class(e)= c("FileNotFound",class(e))
  92. stop(e)
  93. }
  94. else asText <- TRUE
  95. }
  96. #此时asText是FALSE,这个跟我们无关
  97. if(asText && length(file)>1)
  98. file = paste(file, collapse ="\n")
  99. #replaceEntities的默认值是FALSE
  100. old = XML:::setEntitySubstitution(replaceEntities)
  101. #old的值是FALSE
  102. on.exit(XML:::setEntitySubstitution(old), add = TRUE)
  103. #BOMRegExp是一个内置的常量吧
  104. #看名字应该是基于BOM的正则表达式
  105. #因为是FALSE,所以我们也先不去管它
  106. if(asText && length(grep(
  107. sprintf("^%s?\\s*<", XML:::BOMRegExp),
  108. file, perl = TRUE, useBytes = TRUE
  109. )
  110. )==0)
  111. {
  112. if(!isHTML ||(isMissingAsText &&!inherits(file,"AsIs"))){
  113. e = simpleError(paste("XML content does not seem to be XML:",
  114. sQuote(file)))
  115. class(e)= c("XMLInputError",class(e))
  116. (if(isHTML)
  117. warning
  118. else stop)(e)
  119. }
  120. }
  121. #xinclude默认值是TRUE
  122. #以下三个if都是F,所以不管了
  123. if(!is.logical(xinclude)){
  124. xinclude =as.logical(xinclude)
  125. }
  126. if(!asText &&!isURL)
  127. file = path.expand(as.character(file))
  128. if(useInternalNodes && trim){
  129. prevBlanks =.Call("RS_XML_setKeepBlanksDefault",0L,
  130. PACKAGE ="XML")
  131. on.exit(.Call("RS_XML_setKeepBlanksDefault", prevBlanks,
  132. PACKAGE ="XML"), add = TRUE)
  133. }
  134. .oldErrorHandler = XML:::setXMLErrorHandler(error)
  135. #所以,这种点开头的命名是什么鬼?
  136. # class(.oldErrorHandler)
  137. # [1] "list"
  138. on.exit(.Call("RS_XML_setStructuredErrorHandler",.oldErrorHandler,
  139. PACKAGE ="XML"), add = TRUE)
  140. # length(options)是0,所以不执行
  141. if(length(options))
  142. options = sum(options)
  143. #调用一个叫做RS_XML_ParseTree的函数
  144. getAnywhere("RS_XML_ParseTree")
  145. ans <-.Call("RS_XML_ParseTree",as.character(file), handlers,
  146. as.logical(ignoreBlanks),as.logical(replaceEntities),
  147. as.logical(asText),as.logical(trim),as.logical(validate),
  148. as.logical(getDTD),as.logical(isURL),as.logical(addAttributeNamespaces),
  149. as.logical(useInternalNodes),as.logical(isHTML),as.logical(isSchema),
  150. as.logical(fullNamespaceInfo),as.character(encoding),
  151. as.logical(useDotNames), xinclude, error, addFinalizer,
  152. as.integer(options),as.logical(parentFirst), PACKAGE ="XML")
  153. print(ans)
  154. print("-------我是可爱的分割线------------------")
  155. #这里的missing(handlers)我们就不改了哈
  156. #毕竟只要有默认值,他都觉得是TRUE
  157. #和我们的确传递了处理函数的效果TRUE是一样的
  158. if(!missing(handlers)&& length(handlers)&&!as.logical(asTree))
  159. return("呵呵")
  160. if(!isSchema && length(class(ans)))
  161. class(ans)= c(docClass, oldClass(class(ans)))
  162. if(inherits(ans,"XMLInternalDocument")){
  163. addDocFinalizer(ans, addFinalizer)
  164. }elseif(!getDTD &&!isSchema){
  165. print("看我的类型")
  166. print(class(ans))
  167. class(ans)= oldClass("XMLDocumentContent")
  168. print(class(ans))
  169. }
  170. print("看条件判断的逻辑值")
  171. print("1")
  172. print(!missing(handlers)&& length(handlers)&&!as.logical(asTree))
  173. print("2")
  174. print(!isSchema && length(class(ans)))
  175. print("3")
  176. print(inherits(ans,"XMLInternalDocument"))
  177. print("4")
  178. print(!getDTD &&!isSchema)
  179. print(class(ans))
  180. ans
用来调试理解该函数的代码:
R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
 
注释1:关于missing函数
missing(asText)的含义并不是判断asText是否缺失,而是判断asText作为函数的形参,是否已经在函数被调用的时候传入了实参,所以即便形参asText是有默认值的,missing(asText)的返回值结果仍然是TRUE
  1. #例子1
  2. testMissing<-function(a=TRUE,b=FALSE){
  3. if(missing(b))
  4. return("b is missing")
  5. else"b is here "+b
  6. }
  7. testMissing(F)
  8. # [1] "b is missing"
  9. #例子2
  10. if(missing(b))
  11. return("b is missing")
  12. # Error in missing(b) : 'missing' can only be used for arguments
  13. #例子3
  14. b=NULL
  15. if(missing(b))
  16. return("b is missing")
  17. #没有输出
 
注释2 关于if-else语句中else语句的花括号问题
例子1
 
  1. if(isHTML){
  2.     validate = FALSE
  3.     getDTD = FALSE  #从默认值T改为了F
  4.     isSchema = FALSE
  5.     docClass ="HTMLInternalDocument"
  6.   }else{docClass = character()}#否则创建空的
或者
  1. }else docClass = character()
前面的花括号很重要,在交互式模式,R语法分析器用else前面的右花括号来推断这是一个if-else结构,而不是if结构
见《R语言编程艺术中文版》P20
例子2
  1. function(){
  2. if(isHTML){
  3. validate = FALSE
  4. getDTD = FALSE #从默认值T改为了F
  5. isSchema = FALSE
  6. docClass ="HTMLInternalDocument"
  7. }
  8. else docClass = character()#否则创建空的
  9. }
如果在函数体内,这样写是OK的。
否则,会报错:
Error: unexpected 'else' in "  else"
 
 
注释3.1:checkHandlerNames(handlers, "DOM")函数
事实上,必须要借助getAnywhere(checkHandlerNames)的帮助,才能获得其源码
我们可以在最后一行可以看到:<environment: namespace:XML>
所以我们可以通过XML:::checkHandlerNames()调用它
 
  1. function (handlers, id ="SAX") 
  2. {
  3.     if(is.null(handlers))                 #为空,则返回TRUE
  4.         return(TRUE)
  5.     ids = names(handlers)           #取出handlers中的函数名
  6.     i = match(ids,GeneralHandlerNames)  #匹配,返回逻辑值向量
  7.     prob = any(!is.na(i))#任一个回空才是TRUE
  8.     if(prob){
  9.         warning("future versions of the XML package will require names of general handler functions 
  10. to be prefixed by a . to distinguish them from handlers for nodes with those names.  This _may_ affect the ", 
  11.             paste(names(handlers)[!is.na(i)], collapse =", "))
  12.     }
  13.     #任意一个handler中的函数不是函数类型,则抛出异常
  14.     if(any(w <-!sapply(handlers,is.function))) 
  15.         warning("some handlers are not functions: ", paste(names(handlers[w]), 
  16.             collapse =", "))
  17.     #返回TRUE,后续代码继续运行
  18.     !prob
  19. }
  20. <environment: namespace:XML>
 

注释3.2关于GeneralHandlerNames属性

还真是人如其名呀,常用处理函数名
 
  1. > XML:::GeneralHandlerNames
  2. $SAX
  3. [1]"text"                  "startElement"         
  4. [3]"endElement"            "comment"              
  5. [5]"startDocument"         "endDocument"          
  6. [7]"processingInstruction""entityDeclaration"    
  7. [9]"externalEntity"       
  8. $DOM
  9. [1]"text"                  "startElement"         
  10. [3]"comment"               "entity"               
  11. [5]"cdata"                 "processingInstruction"

分别有SAX和DOM两种方式,想起学Java时的DOM和SAX解析没?

 
注释3.3关于match函数
 
 
  1. > h2 <- list(
  2. +   startElement = function(node,...){
  3. +     name <- xmlName(node)
  4. +     if(name %in% c("div","title")){NULL}else{node}
  5. +   },
  6. +   comment = function(node){NULL}
  7. +)
  8. > handlers<-h2
  9. > ids = names(handlers)   
  10. > ids
  11. [1]"startElement""comment"     
  12. > i = match(ids, XML:::GeneralHandlerNames)
  13. > i
  14. [1] NA NA
  15. >?match
  16. starting httpd help server ... done
  17. >!is.na(i)#如果i中有NA,则返回F,没有NA则返回T
  18. [1] FALSE FALSE
  19. > prob = any(!is.na(i))#当i中的任意一个元素都不是NA的时候,prob才返回T
  20. > prob
  21. [1] FALSE

match returns a vector of the positions of (first) matches of its first argument in its second.

math函数实际上返回的是:第一个参数中的元素如果匹配了第二个参数中的值,那么到底匹配的是第二个元素中的第几个,即其位置。
 
  1. > testMatch<-c("a","c")
  2. > testSet <- c('a','b','c')
  3. > match(testMatch,testSet)
  4. [1]13
事实上,看警告信息,未来版本的XML将要求普通的处理函数前面要加一个前缀点(.),以区别于节点他们名字的处理函数(到底是哪些呢?)
future versions of the XML package will require names of general handler functions 
to be prefixed by a . to distinguish them from handlers for nodes with those names.  This _may_ affect the
所以我觉得,这一个match和if判断,其实是一个预设吧,毕竟id='SAX'压根没有用到,应该是作者的编码还没打算在当前版本增加该设定。
其实在useDotNames中已经提到了
 
 
注释4关于inherits函数
inherits indicates whether its first argument inherits from any of the classes specified in the what argument. If which is TRUE then an integer vector of the same length as what is returned. Each element indicates the position in the class(x) matched by the element of what; zero indicates no match. If which is FALSE then TRUE is returned by inherits if any of the names in what match with any class.
what, value
   a character vector naming classes. value can also be NULL.  
inherits函数指明第一个参数是否继承了what参数中的任何一个classs类型
如果which为真,则inherits 返回一个同what一样长度的整型向量,每一个元素表示该位置的元素匹配了what的类型,如果数字是0,则表示不匹配
如果which为假,则当what中任意一个名字匹配了类型时,inherits 返回真
  1. x <-10
  2. class(x)# "numeric"
  3. oldClass(x)# NULL
  4. #看了下文档,我个人觉得oldClass是S语言的余毒啊!!!
  5. inherits(x,"a")#FALSE
  6.  
  7. class(x)<- c("a","b")
  8. # x
  9. # [1] 10
  10. # attr(,"class")
  11. # [1] "a" "b"
  12. #即,x为10这个变量被赋予了两个class,分别名为"a"和 "b"
  13. inherits(x,"a")#TRUE
  14. inherits(x,"a", TRUE)# 1
  15. inherits(x, c("a","b","c"), TRUE)# 1 2 0
 
注释5.1:几行难懂的代码
 
  1.   #以下两行的结果都是integer(0)
  2.   #奇妙~,先保存原有的配置
  3.   oldValidate = XML:::xmlValidity()
  4.   #使用当前的配置
  5.   XML:::xmlValidity(validate)
  6.   #还原
  7.   on.exit(XML:::xmlValidity(oldValidate))
先看下xmlValidity函数
 
 
  1. > XML:::xmlValidity()
  2. integer(0)
  3. > getAnywhere(xmlValidity)
  4. A single object matching ‘xmlValidity’ was found
  5. It was found in the following places
  6.   namespace:XML
  7. with value
  8. function (val = integer(0)) 
  9. {
  10.     .Call("RS_XML_getDefaultValiditySetting",as.integer(val), 
  11.         PACKAGE ="XML")
  12. }
  13. <environment: namespace:XML>
无奈没有更多的信息
RS_XML_getDefaultValiditySetting,看命名应该是获取默认正确的设定
我们再来看:
 
注释5.2.1:关于on.exit函数

on.exit records the expression given as its argument as needing to be executed when the current function exits (either naturally or as the result of an error). This is useful for resetting graphical parameters or performing other cleanup actions.

on.exit记录作为其参数的表达式,在当前的函数退出(自然结束或者出错)时执行,他通常用来充值图形参数,或者执行清理行为。
例子1
 
 
  1. > opar <- par(bg='lightblue')
  2. > on.exit(par(opar))
  3. > plot(c(1,2,3),c(4,5,6))#蓝色背景
  4. > plot(c(1,2,3,4,5),runif(5))#蓝色背景
  5. #此时关闭绘图窗口
  6. > plot(c(1,2,3,4,5),rnorm(5))
  7. #白色背景
例子2
 
  1. plot_with_big_margins <- function(...)
  2. {
  3.   old_pars <- par(mar = c(10,9,9,7))  
  4.   on.exit(par(old_pars))
  5.   plot(...)
  6. }
  7. plot_with_big_margins(with(cars, speed, dist))
  8. R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
  9.  
  10. #不关闭图像窗口,此时再运行如下语句
  11. plot(c(1,2,3),c(4,5,6))
  12. R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
     
  13.  
  14. 对比
  15.  
  16.  
  17. plot_with_big_margins <- function(...)
  18. {
  19.   par(mar = c(10,9,9,7))  
  20.   plot(...)
  21. }
  22. plot_with_big_margins(with(cars, speed, dist))
  23.  
  24. R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
     
  25. #不关闭图像窗口,此时再运行:
  26. plot(c(1,2,3),c(4,5,6))
  27.  
  28. R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
     
并在其所在函数退出后,就失效(注意,par()函数的设定是对当前窗口的所有图标有效,只要窗口没有清理和关闭)。
他所谓的在函数结束时候执行,是说这个on.exit()函数被放在哪个函数里,哪个函数结束的时候,它才执行。
但是代码有点奇怪对不对?
 
  1. old_pars <- par(mar = c(10,9,9,7))  
  2. on.exit(par(old_pars))

不是要还原吗?为什么是把old_pars传递给par()?

事实上:
 
 
  1. > old_pars <- par(mar = c(10,9,9,7))
  2. > old_pars
  3. $mar
  4.  
  5. [1]5.14.14.12.1
  6. > op <- options(stringsAsFactors = FALSE)
  7. > op
  8. $stringsAsFactors
  9. [1] TRUE
我们得到的是尚未改变之前的参数,而不是改变后的参数
我们可以验证这个结论,重启R。
  1. par() #得到的mar是5.14.14.12.1
  2. plot_with_big_margins <- function(...)
  3. {
  4. old_pars <- par(mar = c(10,9,9,7)) #原参数被保存,新参数设置生效
  5.  
  6. print(par()) ,9,9,7
  7.  
  8. on.exit(par(old_pars)) #参数被还原为原来的参数
  9. plot(...)
  10. }
  11. plot_with_big_margins(with(cars, speed, dist))
  12.  
  13. par() #得到的mar是5.14.14.12.1
看到了吧,这就是他重置原参数的功能
例子3
  1. my_plot <- function()
  2. {
  3.   with(cars, plot(speed, dist))
  4. }
  5. save_base_plot <- function(plot_fn, file)
  6. {
  7.   png(file)
  8.   on.exit(dev.off())
  9.   plot_fn()
  10. }
  11. save_base_plot(my_plot,"testcars.png")
下面这篇sof的答案回答的很详细:
 
注释5.2.2:关于on.exit函数
那么
 
 
  1.   #replaceEntities的默认值是FALSE
  2.   old = XML:::setEntitySubstitution(replaceEntities)
  3.   #old的值是FALSE
  4.   on.exit(XML:::setEntitySubstitution(old), add = TRUE)

这几行代码的功能是类似的

 
 
  1. > XML:::setEntitySubstitution
  2. function (val) 
  3. .Call("RS_XML_SubstituteEntitiesDefault",as.logical(val), PACKAGE ="XML")
  4. <environment: namespace:XML>
 
注释5.2.3:关于on.exit函数
  1. .oldErrorHandler = XML:::setXMLErrorHandler(error)
  2.   #所以,这种点开头的命名是什么鬼?
  3.   # class(.oldErrorHandler)
  4.   # [1] "list"
  5.   on.exit(.Call("RS_XML_setStructuredErrorHandler",.oldErrorHandler, 
  6.                 PACKAGE ="XML"), add = TRUE)
  7.  
  8.  
  9. > XML:::htmlErrorHandler
  10. function (msg, code, domain, line, col, level, filename,class="XMLError") 
  11. {
  12.     e = makeXMLError(msg, code, domain, line, col, level, filename, 
  13.         class)
  14.     dom = names(e$domain)
  15.     class(e)= c(names(e$code), sprintf("%s_Error", gsub("_FROM_", 
  16.         "_", dom)),class(e))
  17.     if(e$code == xmlParserErrors["XML_IO_LOAD_ERROR"]) 
  18.         stop(e)
  19. }
  20. <environment: namespace:XML>
  21.  
  22.  
  23. > XML:::setXMLErrorHandler
  24. function (fun) 
  25. {
  26.     prev =.Call("RS_XML_getStructuredErrorHandler", PACKAGE ="XML")
  27.     sym = getNativeSymbolInfo("R_xmlStructuredErrorHandler", 
  28.         "XML")$address
  29.     .Call("RS_XML_setStructuredErrorHandler", list(fun, sym), 
  30.         PACKAGE ="XML")
  31.     prev
  32. }
  33. <environment: namespace:XML>
其实是错误处理的方式的配置
 
注释4:关于判断条件
其实BOMRegExp是一个常量
 
  1. > getAnywhere(BOMRegExp)
  2. A single object matching ‘BOMRegExp’ was found
  3. It was found in the following places
  4.   namespace:XML
  5. with value
  6. [1]"(\\xEF\\xBB\\xBF|\\xFE\\xFF|\\xFF\\xFE)"
 sprintf("^%s?\\s*<", BOMRegExp)是C语言中的打印语句,被包装后用到R里面了,%s代表BOMRegExp的内容。
 
ans <- .Call("RS_XML_ParseTree", as.character(file), handlers, 
               as.logical(ignoreBlanks), as.logical(replaceEntities), 
               as.logical(asText), as.logical(trim), as.logical(validate), 
               as.logical(getDTD), as.logical(isURL), as.logical(addAttributeNamespaces), 
               as.logical(useInternalNodes), as.logical(isHTML), as.logical(isSchema), 
               as.logical(fullNamespaceInfo), as.character(encoding), 
               as.logical(useDotNames), xinclude, error, addFinalizer, 
               as.integer(options), as.logical(parentFirst), PACKAGE = "XML")
 
这里再插入下怎么看.Call的源码的那个sof链接
主要是community wiki的答案
还有Rstudio其实选中函数按下F2可以查看源码的
这篇问答里,提供了R中各种类型的函数的源码查看方式
 《R news article》P43
我也下载了:
R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
 
我也使用照做了,下载了源码看:
 
  1. untar(download.packages(pkgs ="XML",
  2.                         destdir =".",
  3.                         type ="source")[,2])
然后用everything搜索了下,发现在我的文档下,找到src:
在文件内部检索,也只查到了
R自动数据收集第二章HTML笔记2(主要关于htmlTreeParse函数)
 但是再也查不到其他的信息了,ENTYR应该入口吧?小弟的C只是本科时被老师误人子弟的...........(如有读者朋友知道,请评论),21是限定的参数的个数。
所以没有看到最终C的代码,有点遗憾,其实我关于htmlTreeParse函数源码的查看的核心任务没有完成呐
以后不看这么细致了...........抓住主要结构,毕竟C不给看,不给还是学到了一些琐碎的知识点。
 
 
 
 

附件列表

上一篇:GoogleTest 之路3-Mocking Framework


下一篇:在MySql中实现MemberShip的权限管理