rmarkdown rmd 生成word 加分页符

Python015

rmarkdown rmd 生成word 加分页符,第1张

借助2个包 officedown 和 officer ,在需要断页的地方加 r run_pagebreak()

Hello world on page 1

`r run_pagebreak()`

Hello world on page 2

参考:[r - How to add a page break in word document generated by RStudio &markdown - Stack Overflow] https://stackoverflow.com/questions/24672111/how-to-add-a-page-break-in-word-document-generated-by-rstudio-markdown

将用户程序(进程)的 逻辑地址 空间分成若干个 (4KB)并编号,同时将内存的 物理地址 也分成若干个 块或页框 (4KB)并编号

将进程的各个页 离散 地存储在内存的任一物理块中,使得从进程的角度看,认为它有一段 连续的内存 ,进程总是从0号单元开始编址

因此需要建立一个由页到页框的一一映射的关系,这就是 页表

系统会 为每一个进程建立一张页表 ,进程执行时,通过查找进程自己的页表,找到每页在内存中的物理块号,从而保证每个进程都能正确运行

由于页表实现了逻辑地址到物理地址的变换,执行的频率非常高,因此页表大多 驻留在内存中 ,且需要采用硬件实现。在系统中设置一个 页表寄存器(PTR) ,在其中存放页表在内存中的 起始地址 和页表的 长度 ,平时页表始址和长度存放在各进程的PCB中,当调度到某进程时,才将这两个数据装入页表寄存器中。

前20位即为块号,指示物理内存中的块(也称页框)

P - 存在(Present)标志,用于指明表项对地址转换是否有效。P=1表示有效;P=0表示无效。在页转换过程中,如果说涉及的页目录或页表的表项无效,则会导致一个异常。

R/W - 读/写(Read/Write)标志。如果等于1,表示页面可以被读、写或执行。如果为0,表示页面只读或可执行。

1、进程访问某个逻辑地址的数据

2、由逻辑地址的页号(3),以及页表寄存器中的始址,找到页表并找到对应的页表项(3)

3、由页表项上的块号,找到物理内存中的块号

4、由块号,加上逻辑地址的页内地址,实现了对物理地址数据的定位

5、进程访问该逻辑地址对应的物理地址的数据

由上可知,每存取一个数据,需两次访问内存,第一次访问内存中的页表,第二次访问内存中的数据,效率较低

增设一个具有并行查寻能力的特殊高速缓冲寄存器,称为“联想寄存器”或“快表”,IBM中称为TLB,用于存放当前访问的那些页表项

1、进程访问某个逻辑地址的数据

2、由逻辑地址的页号(3),先与高速缓冲寄存器中的所有页号比较,若匹配则直接读出块号,若不匹配则再由上面的2、3步骤执行

3、找到页表项后,将此页表项存入快表中,若快表已满,则系统找出一个认为不再需要的页表项将其换出

4、5步骤相同

IA-32体系结构中,处理器为32位,可寻址2 32 =4GB的虚拟地址空间,若每页大小为4KB,则共分为4GB/4KB=2 20 =1048576页,因此页表中应有1048576项,每个页表项为4B,则一个页表需要4MB的连续的物理内存,每个进程都需要自身的页表占4MB,将导致大量内存用于保存进程的页表

PS:80386处理器为32位,可寻址 4GB 逻辑地址,而当时物理内存只有 4MB ,采用单级页表明显不行

采用两级页表 :每页中存2 10 项,共分为2 10 页,并新增一个页目录表来记录这2 10 页表的地址与信息,因此页目录表大小为2 10 *4B=4KB放在内存中,需要具体的表再由此读入

1、采用离散分配方式代替原来页表需要的连续物理内存

2、将当前需要的部分页表项调入内存,其余页表项仍驻留在磁盘上,需要时再调入

指向原页表项 逻辑地址 结构

PS:位移量W也称为页内地址 页大小4k 所以页内地址需要12位(2 12 =4k)

VS

指向两级页表项 逻辑地址 结构

PS:外层页号也称页目录表( Directory ),外层页内地址也称页表地址( Table )

与页表寄存器(PTR)相同,增设一个外层页表寄存器(CR3),用于存放外层页表的地址

与页表项结构类似。

1、进程访问某个逻辑地址的数据

2、由逻辑地址中的外层页号(Directory),以及外层页表寄存器(CR3)中的外层页表始址,找到二级页表的始址

3、由二级页表的始址,加上逻辑地址中的外层页内地址(Table),找到对应的二级页表中的页表项

4、由页表项中的物理块号,加上逻辑地址中的页内地址(偏移量),实现了对物理地址数据的定位

5、进程访问该逻辑地址对应的物理地址的数据

以上分级解决了原来页表需要连续物理内存空间的问题,接下来解决用较少的内存空间去存放大页表的问题

仅把当前需要的一批页表项调入内存,以后再根据需要陆续调入。因此 页目录表 常驻内存(大小为 4KB ,地址存在CR3寄存器中),而 进程的页表 存于磁盘中,对于页表只需调入一页或几页。由页目录项中的P标记该页表是否在内存中,若不在则产生缺页异常,产生异常中断,请求系统将该页表调入内存

提供一种方案,使分页浏览的报表每页只显示N条记录,最后一页记录条数不足N的,用空行补齐。

示例:

记录共7条,每页显示5条记录:

下载代码(vs2008,需要安装AdventureWorks数据库)

原理:

由于表的分组包含“在起始处分页”和”在结束处分页”的功能,所以我们考虑先将数据分成若干个记录条数为N的组,

再启用“在结束处分页”的功能。

那么,如何分组呢?考虑记录的下标为 0,1,2,3... 的自然数序列,用下标除以N(设为3),得到以下数列:

0 /3 取整:0

1 /3 取整:0

2 /3 取整:0

3 /3 取整:1

4 /3 取整:1

5 /3 取整:1

6 /3 取整:2

所以,我们可以以下标除以3的商的整数值对数据进行分组,则每个分组包含3条数据。当记录条数不能被3整除时,最后一个分组会不足3。分组表达式为 =Int((RowNumber(Nothing) - 1) / N)。

另一个需要考虑的问题是,最后一个分组的记录条数可能不足N,怎样以空白行补齐它呢?考虑TableRow.Visibility.Hidden属性,可通过表达式设置它的状态。为此,我们在详细信息组内额外添加N-1行,如果检查到当前是记录的最后一组最后一条并且该组记录条数不足N时,将Hidden设为Flase;其它情况为True。

表达式为=IIf(RowNumber(Nothing) = CountRows("table1") AndAlso CountRows() <= r,False,True)。其中r从1开始递增。

实现:

1.新建WinForm项目,用报表向导添加一张报表,数据源为AdventureWorksDataSet的Employee数据库表,调整详细信息(这里只选择了表内前面7条数据):

2.(以下都是在报表的设计模式中)在表内添加组,分组表达式为 =Int((RowNumber(Nothing) - 1) / 3),取消包括组头和包括组尾,勾选在结尾处分页:

3.连续插入两行:

4.选择插入的第一行(TableRow3),在Visibility的Hidden属性下选择“表达式”,在打开的文本框内填入

=IIf(RowNumber(Nothing) = CountRows("table1") AndAlso CountRows() <= 1, False, True);同理,在插入的第二行(TableRow4)同位置填入

=IIf(RowNumber(Nothing) = CountRows("table1") AndAlso CountRows() <= 2, False, True):

5.运行程序,可以看到前两页分别只有3条记录,最后一页1条,空白处用空行补齐了:

以MSServer自带Northwind库文件内的Customers及Orders这两个表为例,建立两个报表文件,在查询得出Customers的表数据后,点击此表内的CustomerID数据,报表将转入至下一个报表,并显示与之相关的Orders的数据,即VS中所谓的钻取式报表。

1、打开一个工程,并新建一个From,放入一个Button及一个ReportViewer控件。

2、在工程内新建一个数据源,连接到Northwind库文件,显示Customers及Orders这两个表

3、新建一个报表文件,并以表格的形式来显示数据,将Customers表内的CustomerID、CompanyName及Address三个字段放入,形成一份有表头及数据的简单报表,并将此报表保存命名为customerReport

4、再建一个报表文件,与customerReport相同,也以表格的形式来显示报表,将Orders内的OrderID、CustomerID、ShipName及OrderDate四个字段放入表格内,报表保存为orderReport

5、orderReport的数据源根据主报表customerReport的数据来确定数据源的数据,查看表格的数据集名称,我的显示为NorthwindDataSet_Orders,也可以自己另建一个,具体方法见我的《RDLC报表(二)》

6、再打开customerReport报表,将CustomerID设置成为可点击的索引字段,以便转入下一个报表。选择CustomerID字段,按鼠标右键,在”文本框属性“窗口中,选择”导航“选项卡,在下面的”超链接“中选择”跳至报表“,在报表名称中选择”orderReport“,再按下后面的”参数...“按钮,输入一个参数名称,如customerid,参数值选择=Fields!CustomerID.Value。为了与其它数据相区分,可以将此列数据根据自己的习惯改变颜色或加下划线

7、在orderReport中,设置一个报表参数,与CurtomerReprot中的名称相同,以接收父表中传入的参数

8、新建两个取得数据的方法,一个从Customers中取得数据集,另一个从Orders中取得数据集,且带参数。此两个方法可以自己编写类库来实现,也可以在VS的数据集内添加。为了演示方便,我直接使用了Customers的GetData(),并编写了一个Orders的GetDataByCustomerID(@cid)的方法。

9、在From的Button中编写如下代码:

private void button2_Click(object sender, EventArgs e)

{

NorthwindDataSet.CustomersDataTable dt1 = new NorthwindDataSetTableAdapters.CustomersTableAdapter().GetData()

this.reportViewer1.LocalReport.ReportEmbeddedResource = "TestReport.customerReport.rdlc"

this.reportViewer1.LocalReport.DataSources.Clear()

this.reportViewer1.LocalReport.DataSources.Add(new ReportDataSource("NorthwindDataSet_Customers", dt1))

this.reportViewer1.RefreshReport()

}

10、使用报表的Drillthrough事件,当选择了钻取项时会发生此事件,给下一个报表取值,代码如下:

private void reportViewer1_Drillthrough(object sender, DrillthroughEventArgs e)

{

LocalReport lp = (LocalReport)e.Report

string customerid = lp.GetParameters()["customerid"].Values[0].Trim()

lp.DataSources.Clear()

lp.DataSources.Add(new ReportDataSource("NorthwindDataSet_Orders",

new NorthwindDataSetTableAdapters.OrdersTableAdapter().GetDataByCustomerID(customerid)))

}