65.9K
CodeProject 正在变化。 阅读更多。
Home

为文本添加行号

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.74/5 (14投票s)

2004 年 6 月 25 日

8分钟阅读

viewsIcon

103886

downloadIcon

1105

如何轻松地为源代码或文本添加行号。

Sample screenshot

引言

我有时需要在文档或报告中包含源代码,以便引导读者阅读这些代码。我总是不得不参考一段特定的代码,比如“从第27行开始,到第33行结束,包含”。这将是非常方便的,能够快速轻松地为特定的代码片段添加行号,然后将其粘贴到我的文档中,并引用“第27-33行”而不是一长串描述。

这个小工具中的代码提供了这个功能。您可以将代码片段(实际上是任何文本)复制到应用程序的RichTextBox中,按一个按钮添加行号,经过格式化、带行号的代码就会瞬间出现。这为我节省了大量的时间和精力,尤其是我目前正在写一本包含大量代码示例的书。

背景

尽管这个工具被呈现为一个简单的应用程序,您可以根据需要添加额外的组件,但行号功能的核心 resides(驻留)在一个简单的类中,该类只有两个方法。因此,本文将重点介绍这个类,而不是任何其他标准的应用程序功能(剪切-复制-粘贴等)。此外,我还没有向应用程序添加任何文件I/O操作(打开、保存等),因为我个人不需要它——但您可能想要这样做。

此外,虽然行号类是在Windows应用程序的上下文中提供的,但相关代码可以轻松地通过Web应用程序、Web服务或其他方式使用或改编,以提供类似的功能。

使用应用程序

上面的截图显示了添加行号之前的一段代码。选项字段允许您指定以下参数:

  • 行号填充 – 允许您指定行号与每行转换后的文本开头之间的空格数。
  • 将制表符转换为空格复选框 – 如果选中,所有制表符都将被转换为空格。如果您希望将生成的代码复制到文字处理或桌面出版应用程序中,并且不想处理“复杂的”制表符设置,那么这对于格式化非常有用。
  • 制表符至空格填充 – 如果选中“将制表符转换为空格”复选框,此字段允许您指定用多少个空格替换制表符。

主文本区域包含要转换的源文本。您可以将文本粘贴到此处。转换后的文本将放回此控件中。

要转换文本,请按“添加行号”按钮。主文本区域的内容将被转换后的、带有行号的文本替换。然后,您可以根据需要将转换后的文本复制并粘贴到另一个应用程序中。下面的截图显示了转换后的文本

Application main window

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属性中返回——被放入RichTextBoxText字段,替换了最初粘贴到那里的文本。

关注点

从上面的格式化代码可以看出,这个方便的小工具使得在书中或文档中解释代码变得更加容易。它避免了在正文中散布可能难以理解的代码片段。我当然觉得作者在引用行号时更容易理解代码解释。

尽管使用了RichTextBox,但此应用程序不处理富文本格式。原因有两个:首先,我写的文档/书籍实际上不需要或不使用语法高亮文本;其次,正确处理RTF所需的代码要复杂得多,需要深入了解Rich Text Format文档。这远远超出了本文的范围。这可能看起来像是一种推脱,但在该工具中,目的是以一种清晰的方式展示一种简单的技术。如果您想保留文本的所有字体特征,应该自己进一步研究。

最后,该演示项目是使用Visual Studio .NET 2003构建的。如果您使用的是早期版本的VS.NET,只需使用源代码即可,因为LineNumberBuilder类中没有任何特定于或依赖于VS.NET 2003的内容。

历史

这是此应用程序的第一个(也是唯一一个)版本……除非它坏了,在这种情况下,它就是v0.1.1 Alpha。

© . All rights reserved.