Random Tech Thoughts

The title above is not random

解决 Markdown 转 HTML 中文换行变空格的问题

Update on 2012-04-24: 看到肖之的 给中英文间加个空格 提到了 Jekyll 的 plugin 机制,因此改成用 plugin 机制避免更新 Jekyll 时需要重新 patch 的麻烦。


在 Vim 里编辑文件时我设置行宽不超过 80 个字符,因此编辑 Markdown 文件时一段话中会有多个换行。我用 Octopress 默认配置的 rdiscount 来生成 HTML 文件,这些换行都会保留在最后生成的 HTML 文件中。

浏览器对换行的处理是转成空格,这对英文来说很自然,但对中文就很讨厌了。我在 Stack Overflow 上提了这个问题,从这个详细的回答来看,这是一个历史遗留问题,短期内不会解决。

所以还是自己动手吧。我的解决方法是在生成 HTML 前把所有连续的中文行转成一行。(修改 Markdown 的实现也可以,但 rdiscount 用了 C 写的 Markdown 实现 discount,hack 起来比较麻烦。) 在 Ruby 1.9 中,连接中文行可以很方便的用正则表达式实现,注意开头的 #encoding: UTF-8 是必须的。

1
2
3
4
5
6
7
8
9
#encoding: UTF-8

class String
  han = '\p{Han}|[,。?;:‘’“”、!……()]'
  ChineseRegex = /(#{han}) *\n *(#{han})/m
  def join_chinese
    gsub(ChineseRegex, '\1\2')
  end
end

利用 Jekyll 的插件机制可以在它调用 markdown 转换工具前修改需要转换的文本。把下面的代码放在一个文件中,放在 plugins 目录下即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#encoding: UTF-8

require './plugins/post_filters'

class String
  han = '\p{Han}|[,。?;:‘’“”、!……()]'
  @@chinese_regex = /(#{han}) *\n *(#{han})/m
  def join_chinese!
    gsub!(@@chinese_regex, '\1\2')
  end
end

# Use Jekylly's plugin system to modify the content before invoking rdicount
module Jekyll
  class JoinChineseFilter < PostFilter
    def pre_render(post)
      post.content.join_chinese!
    end
  end
end

需要注意的是分类文章的 rss 需要特殊处理一下,我自己的博客分类做得不好,所以分类 rss 应该没什么价值,所以就懒得处理了。需要处理的可以参考肖之的文章。

下面是原来直接修改 Jekyll 的方法,不再推荐。

可以用来转换文件的代码在 gist 上,但我不希望把 Markdown 源文件变成有着巨长行的文本,所以我修改了 Jekyll,在调用 rdiscount 前对内容调用 join_chinese 方法。需要修改 lib/jekyll/converters/markdown.rb,patch 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
--- bak-markdown.rb 2011-12-23 20:14:24.000000000 +0800
+++ markdown.rb  2011-12-23 20:13:19.000000000 +0800
@@ -1,3 +1,15 @@
+#encoding: UTF-8
+
+class String
+  def join_chinese
+    unless @chinese_regex
+      han = '\p{Han}|[,。?;:‘’“”、!……()]'
+      @chinese_regex = Regexp.new("(#{han})\n(#{han})", Regexp::MULTILINE)
+    end
+    gsub(@chinese_regex, '\1\2')
+  end
+end
+
 module Jekyll

   class MarkdownConverter < Converter
@@ -115,7 +127,7 @@
             }).to_html
           end
         when 'rdiscount'
-          RDiscount.new(content, *@rdiscount_extensions).to_html
+          RDiscount.new(content.join_chinese, *@rdiscount_extensions).to_html
         when 'maruku'
           Maruku.new(content).to_html
       end

用 Markdown 来写这种类型的 blog 真是舒服多了。

Comments