将自由文本重新格式化为固定格式文本(C#)

时间:2011-01-20 20:16:41

标签: c# .net

我有一个看似很直接的问题,但我找不到一个干净简单的解决方案。

  • 我有一些自由格式化的文字。此文本可能很长,包含各种长度(> 120个字符),段落和空行的行。

  • 我需要以固定格式呈现此文本(例如,120行字符行和25行行页面)。但要将原始格式保留在段落和空行中。

分页符不应位于单词的中间。理想情况下,应该放置一个分页符,以便我们避免页面底部的新段落的单行,而是将整个段落移动到下一页等。

简化样本(输入文本):


Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec at magna at tellus vehicula eleifend. Vivamus at est erat. Phasellus eget tincidunt tellus. Integer ultrices dolor a magna congue imperdiet. 

Duis est sem, aliquet id fermentum sed, mollis nec metus. Phasellus porttitor porttitor sodales. Aliquam tincidunt convallis massa, sed tempus erat ornare in. Sed scelerisque, lorem accumsan imperdiet accumsan, mauris turpis molestie augue, vehicula egestas tellus quam ac nulla. 

In porta augue ac dolor imperdiet semper. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Proin lacus neque, tempor nec feugiat sed, posuere sed lorem. 

Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nulla metus neque, volutpat vitae pharetra rutrum, malesuada in dolor. 

“固定”宽度,用分页符格式化(程序输出):


Lorem ipsum dolor sit amet, consectetur adipiscing elit. 
Donec at magna at tellus vehicula eleifend. Vivamus at 
est erat. Phasellus eget tincidunt tellus. Integer 
ultrices dolor a magna congue imperdiet. 

Duis est sem, aliquet id fermentum sed, mollis nec metus. 
Phasellus porttitor porttitor sodales. Aliquam tincidunt 
convallis massa, sed tempus erat ornare in. Sed scelerisque, 
lorem accumsan imperdiet accumsan, mauris turpis molestie 
augue, vehicula egestas tellus quam ac nulla. 
[pagebreak]
In porta augue ac dolor imperdiet semper. Vestibulum ante 
ipsum primis in faucibus orci luctus et ultrices posuere 
cubilia Curae; Proin lacus neque, tempor nec feugiat sed, 
posuere sed lorem. 

Class aptent taciti sociosqu ad litora torquent per conubia 
nostra, per inceptos himenaeos. Nulla metus neque, volutpat 
vitae pharetra rutrum, malesuada in dolor. 

有人有什么想法吗?

3 个答案:

答案 0 :(得分:5)

第一阶段

  1. 将文字读入一个字符串。
  2. 将换行符分隔为换行符(\ n)上的数组(lines [])。
  3. 第2阶段

    1. 初始化一个stringbuilder。
    2. 遍历行集合,并将每行拆分为空格字符上的单词数组。然后遍历单词数组并将每个单词追加到字符串构建器。当行长度超过阈值时,插入换行符。当你在lines数组的末尾时,检查stringbuilder!EndsWith换行符(你的最后一行恰好是阈值长度,然后为段落中添加两个换行符。

答案 1 :(得分:1)

假设您使用的是单个非比例字体,(因此行的宽度指定为字符数而不是厘米数)...

您的问题有几个部分。

首先,您希望将文本段落自动换行为不超过 n 个字符的行。基本方法是首先处理每个段落,使其成为单行文本(如果您还没有该表单中的输入),则使用变量作为“游标” - 将其放在索引 n 然后向后退一步,直到找到一些空格。这是适合该行的最后一个单词的结尾。将此行复制到行列表中,然后重复以将字符串分解为单词包装段落。

(注意:在某些情况下,您必须在此处理:您可能必须使用标点字符和连字符进行拆分,并且您可能必须处理比格式化宽度更长的“单个单词”。高级格式化您可能想要添加连字词典,以便您可以用连字符分割单词

获得段落后,您需要应用类似的算法将文档分成一页一页。再次,从 m 行的“光标”位置开始进入行列表(其中 m 是页面长度)。但是,您需要“寡妇和孤儿”控制,因此您需要添加一些逻辑,例如:

  • 如果页面的第一行是空白的,请删除它们(这样您就不会在页面顶部获得空格)。这当然意味着更多行会流到页面底部。
  • 如果页面的第一行是段落的结尾(即页面的第二行是空白的),那么您可能希望通过将上一页的最后一行移动到此页面的顶部来修复孤立行。页。 (但只有当孤儿是一个段落而不仅仅是一个非常短的段落时!)
  • 如果页面的最后一行是新段落的开头(即倒数第二行是空白的),则将其移至下一页的开头。

从根本上讲,这个过程非常简单,但是有很多关于你想要如何处理自动换行和换页的复杂性。一个简单的算法不会花费很长时间来敲门,但你可以花很多时间调整和改进它以达到“最好”(在你眼中,最少)的结果。

答案 2 :(得分:0)

好的,如果我们将文本分成单词和段落,那么我们可以简单地逐字添加输出:

const int linewidth = 50;

static void Main(string[] args) {

  using(StreamReader r = new StreamReader("text1.txt")) {
    using(StreamWriter w = new StreamWriter("text2.txt")) {

      int written = 0;

      while(true) {
        string word = ReadWord(r);
        if(word == null) break; //end of file
        if(word == "") {
          //end of paragraph
          w.Write("\r\n\r\n");
          written = 0;
        }

        if(written + word.Length > linewidth) {
          //endline
          w.Write("\r\n");
          written = 0;
          int i = 0;
          while(word[i] == ' ') i++;
          w.Write(word.Substring(i));
          written = word.Length - i;
        } else {
          w.Write(word);
          written += word.Length;
        }
      }
    }
  }
}

所以我们需要一些聪明的“文字阅读器”:

static int c = -1;

static string ReadWord(StreamReader r) {
  string word = "";
  bool started = false;

  if(c == -1) c = ReadChar(r);

  while(true) {
    if(c == -1) {
      //eof
      if(word == "") return null;
      return word;
    }
    word += (char)c;
    c = r.Read();
    if(c != ' ') started = true;
    else if(started) break;
  }

  return word;
}

这个单词读者需要一个智能字符阅读器,它将所有行结束视为空格并将空行识别为段落:

static bool lineend = false;

static int ReadChar(StreamReader r) {
  int c = r.Read();
  if(c == '\n') c = r.Read();
  if(c == '\r') {
    if(lineend) return '\r';
    lineend = true;
    return ' ';
  }
  lineend = false;
  return c;
}

正如您所看到的,我没有使用内部数组缓冲区,因此该程序可用于任何大型文件,但可能不如内存中的字符串算法快。

长于行的单词会写入自己的行(请参阅Main)。

只有空格和CRLF被视为单词分隔符。在实际情况中,您应该将此扩展到TAB或其他空格。