为文本添加行号






3.74/5 (14投票s)
2004 年 6 月 25 日
8分钟阅读

103886

1105
如何轻松地为源代码或文本添加行号。
引言
我有时需要在文档或报告中包含源代码,以便引导读者阅读这些代码。我总是不得不参考一段特定的代码,比如“从第27行开始,到第33行结束,包含”。这将是非常方便的,能够快速轻松地为特定的代码片段添加行号,然后将其粘贴到我的文档中,并引用“第27-33行”而不是一长串描述。
这个小工具中的代码提供了这个功能。您可以将代码片段(实际上是任何文本)复制到应用程序的RichTextBox
中,按一个按钮添加行号,经过格式化、带行号的代码就会瞬间出现。这为我节省了大量的时间和精力,尤其是我目前正在写一本包含大量代码示例的书。
背景
尽管这个工具被呈现为一个简单的应用程序,您可以根据需要添加额外的组件,但行号功能的核心 resides(驻留)在一个简单的类中,该类只有两个方法。因此,本文将重点介绍这个类,而不是任何其他标准的应用程序功能(剪切-复制-粘贴等)。此外,我还没有向应用程序添加任何文件I/O操作(打开、保存等),因为我个人不需要它——但您可能想要这样做。
此外,虽然行号类是在Windows应用程序的上下文中提供的,但相关代码可以轻松地通过Web应用程序、Web服务或其他方式使用或改编,以提供类似的功能。
使用应用程序
上面的截图显示了添加行号之前的一段代码。选项字段允许您指定以下参数:
- 行号填充 – 允许您指定行号与每行转换后的文本开头之间的空格数。
- 将制表符转换为空格复选框 – 如果选中,所有制表符都将被转换为空格。如果您希望将生成的代码复制到文字处理或桌面出版应用程序中,并且不想处理“复杂的”制表符设置,那么这对于格式化非常有用。
- 制表符至空格填充 – 如果选中“将制表符转换为空格”复选框,此字段允许您指定用多少个空格替换制表符。
主文本区域包含要转换的源文本。您可以将文本粘贴到此处。转换后的文本将放回此控件中。
要转换文本,请按“添加行号”按钮。主文本区域的内容将被转换后的、带有行号的文本替换。然后,您可以根据需要将转换后的文本复制并粘贴到另一个应用程序中。下面的截图显示了转换后的文本
LineNumberBuilder 类
本文下载中包含的主要工作类名为LineNumberBuilder
。它包含四个属性、一个公共方法和一个私有方法。这些属性(使用通常的“getter”和“setter”访问)保存您在主窗口选项字段中设置的值,以及您粘贴到RichTextBox
中的源文本。下面是该类的一个快照(getter和setter代码已从列表中删除)
1: public class LineNumberBuilder
2: {
3: private string inputText;
4: private int lineNumberPaddingWidth;
5: private bool convertTabsToSpaces;
6: private int tabToSpacesWidth;
7:
8:
9: public LineNumberBuilder()
10: {
11: }
12:
13: public void ConvertText(){...}
14:
15: private string GetFormattedLineNumber( int lineNumber,
int numberMaxWidth, string padding ){...}
16: }
下表显示了私有类变量如何映射到公共类属性。
类变量到属性映射 | ||
---|---|---|
类变量 | 类属性 | |
inputText |
-> | 文本 |
lineNumberPaddingWidth |
-> | LineNumberPaddingWidth |
convertTabsToSpaces |
-> | ConvertTabsToSpaces |
tabToSpacesWidth |
-> | TabToSpacesWidth |
公共方法ConvertText()
负责实际转换源文本并添加格式化的行号。私有方法GetFormattedLineNumber()
由ConvertText()
调用,用于为文本中的每一行创建和格式化行号。
让我们快速浏览一下这个类中的方法,从GetFormattedLineNumber()
方法开始。我们将在文章后面看到应用程序如何使用这个类。
GetFormattedLineNumber() 方法
这个私有方法由公共ConvertText()
方法调用。它的任务是为特定行构建行号文本。它将格式化的行号作为字符串返回给调用例程。
1: private string GetFormattedLineNumber( int lineNumber,
int maxNumberWidth, string padding )
2: {
3: StringBuilder line = new StringBuilder();
4:
5: line.Append( lineNumber.ToString().PadLeft( maxNumberWidth, ' ' ) );
6: line.Append( ":" );
7: line.Append( padding );
8:
9: return line.ToString();
10: }
该方法有三个参数:第一个参数包含我们要格式化的当前行号;第二个参数包含最长行号字符串的长度——也就是说,如果源文本有200行,那么最长行号字符串的长度就是3;第三个参数是一个字符串,包含我们要添加的填充——它分隔了行号和冒号以及某行的文本开头。
首先,我们创建一个本地StringBuilder
对象line
,用于在构建行号文本时存储它。
第5行将当前行号附加到line
对象,并根据maxNumberWidth
参数指定的进行左填充。这确保行号右对齐。如果您希望它是左对齐的,请将PadLeft
更改为PadRight
。
第6行将冒号附加到填充的行号,第7行附加额外的填充——用户通过主窗口选项中的“行号填充”字段指定填充宽度。
本方法第9行简单地将格式化的行号文本作为字符串返回。
ConvertText() 方法
此方法处理源文本,在每行的开头插入行号。
1: public void ConvertText()
2: {
3: StringBuilder output = new StringBuilder();
4:
5: try
6: {
7: char[] end_of_line = {(char)10};
8: string[] lines = this.Text.Split( end_of_line );
9:
10: int line_count = lines.GetUpperBound(0)+1;
11: int linenumber_max_width = line_count.ToString().Length;
12: string padding = new String( ' ', this.LineNumberPaddingWidth);
13:
14: for ( int i=0; i<line_count; i++ )
15: {
16: output.Append( this.GetFormattedLineNumber( i+1,
linenumber_max_width, padding ) );
17: output.Append( lines[i] );
18: output.Append( "\r\n" );
19: }
20:
21: if ( this.ConvertTabsToSpaces )
22: {
23: string spaces = new String( ' ', this.TabToSpacesWidth);
24: output = output.Replace( "\t", spaces );
25: }
26: }
27: catch ( Exception e )
28: {
29: output.Append( e.Message );
30: }
31:
32: this.Text = output.ToString();
33: }
第3行创建一个新的StringBuilder
实例,我们用它来存储转换后的文本,同时构建每一行。
第7-8行获取源文本(包含在类实例的Text
属性中)并将其拆分为一个字符串数组——每个数组项代表一行文本。end_of_line
变量用于指定我们要分割文本的字符。在这种情况下,它使用\n
换行符(ASCII值为10)。(作为家庭作业,您可能希望允许用户指定此分隔符,也许作为主窗口选项字段中的一个字段/属性。)
第10行简单地计算字符串数组中的项数。第11行将line_count
转换为字符串并获取其长度。这用于正确填充行号,以避免不必要的、丑陋的缩进,并使分隔行号和行文本的冒号垂直对齐。第12行创建一个新字符串,其中包含分隔行号和行文本开头的空格。
第14-19行在for
循环中执行主要格式化操作,逐行处理源文本。
在for
循环中,第16行将行号附加到output
对象。它调用私有方法GetFormattedLineNumber()
,该方法创建行号并添加尾随的冒号以及任何必要的填充。
第17行附加源文本行(包含在引用的字符串数组项lines[i]
中)。
第18行简单地将行尾字符添加到行中,以便在转换完成后保留原始的换行符。
如果用户选择了将制表符转换为空格,第21-25行将处理此问题。创建一个包含用户通过主窗口选项中的“制表符至空格填充”字段指定的空格数的新字符串。然后,output
对象中包含的任何制表符字符都将被替换为spaces
字符串的内容。
最后,转换后的文本被放入类实例的Text
属性中(第32行)。
使用LineNumberBuilder类
当用户在主窗口中按下“添加行号”按钮时,该按钮的单击事件会调用一个名为ExecuteLineNumbering()
的例程。下面是这个例程的样子
1: private void ExecuteLineNumbering()
2: {
3: if ( this.SourceMemo.Text.Length > 0 )
4: {
5: LineNumberBuilder builder = new LineNumberBuilder();
6:
7: builder.Text = this.SourceMemo.Text;
8: builder.LineNumberPaddingWidth =
Convert.ToInt32( this.eLineNumberPadding.Text );
9: builder.ConvertTabsToSpaces = this.cbConvertToSpaces.Checked;
10: builder.TabToSpacesWidth =
Convert.ToInt32( this.eTabPadding.Text );
11:
12: builder.ConvertText();
13: this.SourceMemo.Text = builder.Text;
14: }
15: }
首先,我们在(第3行)检查SourceMemo
RichTextBox
中是否有任何文本。如果有,我们就创建一个LineNumberBuilder
类的新实例(builder
)。在第7-10行中,我们用主窗口字段中的数据填充此对象的属性,包括RichTextBox
的内容。(为了提高代码可读性,并且由于懒惰,没有提供错误检查来捕获无效字段值——这留给您作为家庭作业。)
最后,在第12行,我们调用builder.ConvertText()
方法对行进行编号。在第13行,转换后的行——现在在我们的builder.Text
属性中返回——被放入RichTextBox
的Text
字段,替换了最初粘贴到那里的文本。
关注点
从上面的格式化代码可以看出,这个方便的小工具使得在书中或文档中解释代码变得更加容易。它避免了在正文中散布可能难以理解的代码片段。我当然觉得作者在引用行号时更容易理解代码解释。
尽管使用了RichTextBox
,但此应用程序不处理富文本格式。原因有两个:首先,我写的文档/书籍实际上不需要或不使用语法高亮文本;其次,正确处理RTF所需的代码要复杂得多,需要深入了解Rich Text Format文档。这远远超出了本文的范围。这可能看起来像是一种推脱,但在该工具中,目的是以一种清晰的方式展示一种简单的技术。如果您想保留文本的所有字体特征,应该自己进一步研究。
最后,该演示项目是使用Visual Studio .NET 2003构建的。如果您使用的是早期版本的VS.NET,只需使用源代码即可,因为LineNumberBuilder
类中没有任何特定于或依赖于VS.NET 2003的内容。
历史
这是此应用程序的第一个(也是唯一一个)版本……除非它坏了,在这种情况下,它就是v0.1.1 Alpha。