2008年01月07日 Monday , 1,277 次点击

    图形界面其实写起来没有一点儿技术含量,但就是麻烦,需要较多的时间,加上我又没有时间,所以Lenin's Blog Writer要形成战斗力还需要一段时间。在此之前的文章就靠这个FormatDraft了。

    写了有段时间了,不是什么新东西,只是最近决定不再使用Coolcode插件,而是启用更强大的IG Syntax Hilite,所以需要对程序作一些改动。程序是为方便将我写好的文本格式的文件转换成HTML格式文件而写的一个小程序,是Lenin's Blog Writer投入使用前的一个替代品,对文章的排版格式要求比较严格,所以我觉得只适合我自己,对别人没有实用意义,在此仅作观赏,不提供程序的下载了。

    之所以需要将文本文件转换成HTML再复制其源码到博客,实在是迫不得已。普通的文章,几个汉字也就罢了,不过作为技术博客,文章中经常含有大量各种源代码,直接贴到博客里面容易出问题,所以只好先转换成网页格式再发布其源码了。而我写起文章容易如滔滔江水连绵不绝,这样一来如果用网页编辑器一点一点的加HTML代码的话会很累,所以写个程序来做这些事儿。

    FormatDraft可以对两种格式的文章作出处理。

    一种是普通格式的文件,我写文章的格式是:文件第一行是文章标题;文章标题与段落之间以及段落与段落之间用一个空行隔开;每个段落段首使用一个Tab来缩进;此外的内容以缩进宽度来表示上下段落之间的关系,即如果本段上面一段缩进了一个Tab宽度,而本段缩进了两个Tab宽度,那么本段是上面一段的子内容。

    另一种是我记《Vi IMproved》的笔记时用的格式:除了普通文章中的那几条之外,使用“<>”括起来数字表示小标题;使用“()”括起来数字表示小小标题。

    针对以上格式,FormatDraft做出如下处理:使用“<!-- -->”将文章标题括起来成为注释;文章标题与第一段之间的空行以“<p>”代替;段落与段落之间的空行以“</p><p>”代替;对于所有的缩进,以四个“&nbsp;”来代替每一个缩进。另外,程序会试图从“iconograph.properties”配置文件里读取插图的地址信息,并将其插入文章标题下的一行中,右对齐。vim笔记格式文件的插图地址信息定义格式为“vim=http://图片网址”,普通格式文件插图地址信息定义格式为“normal=http://图片网址”。

    程序有意思的地方在于可以在文章中使用“忽略标记”,所有位于一对“忽略标记”之间的内容都不会被FormatDraft处理,而是被原封不动地输出到结果中,“忽略标记”格式为“<标记名>”或者"[标记名]"。“忽略标记”的结束标记格式为“</标记名>”或者"[/标记名]"。允许忽略标记中添加属性,即如果定义一个“忽略标记”为“<coolcode>”,那么“<coolcode>”与“</coolcode>”之间或者“<coolcode lang="java" download="sample.java">”与“</coolcode>”之间的内容都会被直接输送到作为结果的网页文件中。“忽略标记”在程序所在目录下的“EscapeMarkers.properties”文件中定义,格式为“<标记名>=</标记名>”或者"[标记名]=[/标记名]"。

    文章中所有的“&”、“<”、“>”和英文双引号会被替换成HTML实体,分别为“&amp;”“&lt;”“&gt;”和“&quot;”,位于“忽略标记”内的内容不会被替换。

    在vim模式中,小标题会被“<b></b>”括起来,即加粗;不包含中括号的小小标题会被“<i></i>”括起来,即斜体。

    最后,使用“-normal”的参数使程序把文件以普通模式处理,使用“-vim”使程序把文件当作我的vim笔记格式的文件来处理。模式参数后可以跟一个或两个编码方案的名称作参数,缺省以UTF-8编码处理文件。命令格式为:

//什么参数都不加,直接添加文本文件的路径,以普通模式处理文件,以UTF-8解码文本文件,以UTF-8编码HTML文件
java FormatDraft /home/lenin/drafts/sample.txt

//添加一个模式参数,以该模式处理该文本文件,以UTF-8解码文本文件,以UTF-8编码HTML文件
java FormatDraft -vim /home/lenin/drafts/sample.txt

//添加一个模式参数和一个编码参数,以该模式处理该文本文件,以该编码解码文本文件,以UTF-8编码HTML文件
java FormatDraft -vim gbk /home/lenin/drafts/sample.txt

//添加一个模式参数和两个编码参数,以该模式处理该文本文件,以第一个编码解码文本文件,以第二个编码编码HTML文件
java FormatDraft -vim utf-8 gbk /home/lenin/drafts/sample.txt

JAVA:
  1. /*
  2.  *      两种模式转换文章到网页格式:
  3.  *
  4.  *      两种模式共有的处理方式:
  5.  *          文件第一行为标题,标题与段落之间、段落与段落之间用空行隔开
  6.  *          从配置文件“iconograph.properties”中读取针对该类文章的插图地址并添加到文章标题下,如果地址错误或不存在,返回零长度串
  7.  *          从配置文件“EscapeMarkers.properties”中读取的“忽略标记”之间的内容都会原封不动地输出到结果文件中
  8.  *          此外的内容以行的缩进深度来标识内容之间的关系。
  9.  *      转换后保存在原文本文件所在的目录,与文本文件同名,后缀是“.html”
  10.  *      命令格式:
  11.  *          Linux :
  12.  *              java FormatDraft -ARGUMENT /home/lenin/drafts/sample.txt
  13.  *          Windows :
  14.  *              java FormatDraft -ARGUMENT c:\drafts\sample.txt
  15.  *
  16.  *      第一种转换我的普通文章格式的文本文件到网页格式:
  17.  *          参数为:-normal
  18.  *
  19.  *      第二种转换我的Vi IMproved笔记格式的文本文件到网页格式:
  20.  *          参数为:-vim
  21.  *          小标题为以方括号括起来的数字,小小标题为以圆括号括起来的数字
  22.  *
  23.  */
  24.  
  25. import java.io.File;
  26. import java.io.FileInputStream;
  27. import java.io.BufferedReader;
  28. import java.io.PrintWriter;
  29. import java.io.InputStreamReader;
  30. import java.io.IOException;
  31. import java.util.Properties;
  32. import java.util.Set;
  33. import java.util.HashSet;
  34. import java.util.Enumeration;
  35.  
  36. public class FormatDraft
  37. {
  38.     private String VERSION = "FormatDraft Version 0.3";
  39.     private String DATE = "2008.01.06";
  40.     private String unclosedEscapeMarker = "";       //暂存“忽略标记”的字符串
  41.     private String temp;    //暂存当前读入行的字符串
  42.     private String TABWIDTH = "&nbsp;&nbsp;&nbsp;&nbsp;";       //用于填充一个制表符宽度的四个空格
  43.     private StringBuffer sb = new StringBuffer();       //用于读入待转换文件
  44.     private StringBuffer outputFileName = new StringBuffer();           //用于生成转换后的文件的文件名
  45.     private boolean title = true;       //标识当前读入行是否是文章标题
  46.     private boolean theFirstNullLine = true;        //标识当前读入的空行是否是文章标题下的第一个空行
  47.     private Set escapeMarkers;      //用于暂存从配置文件中读入的“忽略标记”
  48.  
  49.     //转换Vim笔记格式文件的方法
  50.     private void transformVimDraft(String path,String decoder,String encoder) throws IOException{
  51.         File sourceFile = new File(path);
  52.         if(!sourceFile.exists()){
  53.             System.out.println("The file does not exist !");
  54.             System.exit(0);
  55.         }
  56.         if(!sourceFile.getName().toLowerCase().endsWith(".txt")){
  57.             System.out.println("Only text files with the \".txt\" suffix can be dealt with !");
  58.             System.exit(0);
  59.         }
  60.  
  61.         BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile),decoder));
  62.         while((temp=br.readLine())!=null){
  63.             //若该行为“<...>”或"[...]"格式且成功读入“忽略标记”的配置文件且该行属于“忽略标记”,则将其暂存到unclosedEscapeMarker中
  64.             if((temp.trim().matches("^\\<[^/\\<\\>\\[\\]]*\\>$") || temp.trim().matches("^\\[[^/\\<\\>\\[\\]]*\\]$")) && escapeMarkers.size()>0 && escapeMarkers.contains(peeler(temp))){
  65.                 unclosedEscapeMarker = peeler(temp);
  66.             //否则,如果该行为“</...>”或"[/...]"格式,且成功读入“忽略标记”的配置文件,且unclosedEscapeMarker中已存有“忽略标记”,且该行与已暂存的“忽略标记”匹配,则清空unclosedEscapeMarker,并保存该行到sb,之后跳出本次循环
  67.             }else if((temp.trim().matches("^\\</.*\\>$") || temp.trim().matches("^\\[/.*\\]$")) && escapeMarkers.size()>0 && unclosedEscapeMarker.length()> 0 && temp.trim().equals(readClosingEscapeMarker(unclosedEscapeMarker))){
  68.                 unclosedEscapeMarker = "";
  69.                 sb.append(temp+"\n");
  70.                 continue;
  71.             }
  72.             if(unclosedEscapeMarker.length() != 0){
  73.                 sb.append(temp+"\n");
  74.                 continue;
  75.             }
  76.             temp = temp.replaceAll("&","&amp;");        //将“&”转换成实体
  77.             temp = temp.replaceAll("<","&lt;"); //将HTML文件中的尖括号替换为实体
  78.             temp = temp.replaceAll(">","&gt;");
  79.             temp = temp.replaceAll("\"","&quot;");      //将“"”转换成实体
  80.             if(title == true){
  81.                 sb.append("<!--"+temp+"-->\n"); //将文章标题用注释括起来
  82.                 sb.append(readIconographURL("vim","right"));     //文章标题下添加插图
  83.                 title = false;
  84.             }else if(temp.matches("^\\t*$")){
  85.                 if(theFirstNullLine == true){
  86.                     sb.append("<p>");               //如果是文章标题下的第一个空行,只添加一个分段符
  87.                     theFirstNullLine = false;
  88.                 }else{
  89.                     sb.append("</p><p>");       //如果是普通空行,添加一个分段结束符和一个分段符
  90.                 }
  91.             }else if(temp.matches("^\\&lt;.*$")){
  92.                 sb.append("<b>"+temp.trim()+"</b><br>\n");      //如果是尖括号开头的小标题行,使之加粗
  93.             }else if(temp.matches("^\\t*\\(.*$")){
  94.                 if(!temp.matches("^.*\\[.*\\].*$"))
  95.                     sb.append(TABWIDTH+"<i>"+temp.trim()+"</i><br>\n");     //如果是小括号开头的标题,且其中没有方括号括起来的命令,去掉行两端空白字符,缩进四个空格的宽度,使用斜体表示
  96.                 else
  97.                     sb.append(TABWIDTH+temp.trim()+"<br>\n");       //如果有方括号,使用普通字体
  98.             }else if(temp.matches("^\\t\\S.*$")){
  99.                 sb.append(TABWIDTH+temp.trim()+"<br>\n");       //此外,如果以上条件都不满足,那么如果行开头有一个空白字符,则缩进四个空格
  100.             }else if(temp.matches("^\\t{2}\\S.*$")){
  101.                 sb.append(TABWIDTH+TABWIDTH+temp.trim()+"<br>\n");      //如果行开头有两个空白字符,则缩进八个空格宽度
  102.             }else if(temp.matches("^\\t{3}\\S.*$")){
  103.                 sb.append(TABWIDTH+TABWIDTH+TABWIDTH+temp.trim()+"<br>\n");     //如果行开头有三个空白字符,则缩进十二的空格宽度
  104.             }else if(temp.matches("^\\t{4}\\S.*$")){
  105.                 sb.append(TABWIDTH+TABWIDTH+TABWIDTH+TABWIDTH+temp.trim()+"<br>\n");        //如果行开头有四个空白字符,则缩进十六的空格宽度
  106.             }else if(temp.matches("^\\t{5}\\S.*$")){
  107.                 sb.append(TABWIDTH+TABWIDTH+TABWIDTH+TABWIDTH+TABWIDTH+temp.trim()+"<br>\n");       //如果行开头有五个空白字符,则缩进二十个空格宽度
  108.             }else{
  109.                 System.out.println("Wrong file format !");
  110.                 System.exit(0);
  111.             }
  112.         }
  113.         sb.append("</p>");
  114.  
  115.         outputFileName.append(sourceFile.getPath().substring(0,sourceFile.getPath().toLowerCase().lastIndexOf(".txt"))).append(".html");        //将原文本文件的名字中的“.txt”后缀去掉,换成“.html”
  116.         File outputFile = new File(outputFileName.toString());
  117.         if(outputFile.exists())
  118.             outputFile.delete();
  119.         PrintWriter pw = new PrintWriter(outputFile,encoder);
  120.         pw.println(sb.toString());
  121.         pw.flush();
  122.         pw.close();
  123.         System.out.println("Transformation succeed !");
  124.     }
  125.  
  126.     //转换普通格式文件方法
  127.     private void transformNormalDraft(String path,String decoder,String encoder) throws IOException{
  128.         File sourceFile = new File(path);
  129.         if(!sourceFile.exists()){
  130.             System.out.println("File missing !");
  131.             System.exit(0);
  132.         }
  133.         if(!sourceFile.getName().toLowerCase().endsWith(".txt")){
  134.             System.out.println("Only text files with the \".txt\" suffix can be dealt with !");
  135.             System.exit(0);
  136.         }
  137.  
  138.         BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(sourceFile),decoder));
  139.         while((temp=br.readLine())!=null){
  140.             //若该行为“<...>”或"[...]"格式且成功读入“忽略标记”的配置文件且该行属于“忽略标记”,则将其暂存到unclosedEscapeMarker中
  141.             if((temp.trim().matches("^\\<[^/\\<\\>\\[\\]]*\\>$") || temp.trim().matches("^\\[[^/\\<\\>\\[\\]]*\\]$")) && escapeMarkers.size()>0 && escapeMarkers.contains(peeler(temp))){
  142.                 unclosedEscapeMarker = peeler(temp);
  143.             //否则,如果该行为“</...>”或"[/...]"格式,且成功读入“忽略标记”的配置文件,且unclosedEscapeMarker中已存有“忽略标记”,且该行与已暂存的“忽略标记”匹配,则清空unclosedEscapeMarker,并保存该行到sb,之后跳出本次循环
  144.             }else if((temp.trim().matches("^\\</.*\\>$") || temp.trim().matches("^\\[/.*\\]$")) && escapeMarkers.size()>0 && unclosedEscapeMarker.length()> 0 && temp.trim().equals(readClosingEscapeMarker(unclosedEscapeMarker))){
  145.                 unclosedEscapeMarker = "";
  146.                 sb.append(temp+"\n");
  147.                 continue;
  148.             }
  149.             //如果还没有找到“忽略标记”的结束标记,则对当前行不处理,并将其添加到sb中
  150.             if(unclosedEscapeMarker.length() != 0){
  151.                 sb.append(temp+"\n");
  152.                 continue;
  153.             }
  154.             temp = temp.replaceAll("&","&amp;");        //将“&”转换成实体
  155.             temp = temp.replaceAll("<","&lt;"); //将HTML文件中的尖括号替换为实体
  156.             temp = temp.replaceAll(">","&gt;");
  157.             temp = temp.replaceAll("\"","&quot;");      //将“"”转换成实体
  158.             if(title==true){
  159.                 sb.append("<!--"+temp.trim()+"-->\n");  //将文章标题用注释括起来
  160.                 sb.append(readIconographURL("normal","right"));  //文章标题下添加插图
  161.                 title = false;
  162.             }else if(temp.matches("^\\t*$")){
  163.                 if(theFirstNullLine==true){             //如果是文章标题下的空行,……
  164.                     sb.append("<p>\n");
  165.                     theFirstNullLine = false;
  166.                 }else{
  167.                     sb.append("</p><p>\n");         //如果是普通空行,……
  168.                 }
  169.             }else if(temp.matches("^\\t\\S.*$")){
  170.                 sb.append(TABWIDTH+temp.trim()+"\n");       //此外,如果以上条件都不满足,那么如果行开头有一个空白字符,则缩进四个空格
  171.             }else if(temp.matches("^\\t{2}\\S.*$")){
  172.                 sb.append(TABWIDTH+TABWIDTH+temp.trim()+"\n");      //如果行开头有两个空白字符,则缩进八个空格宽度
  173.             }else if(temp.matches("^\\t{3}\\S.*$")){
  174.                 sb.append(TABWIDTH+TABWIDTH+TABWIDTH+temp.trim()+"\n");     //如果行开头有三个空白字符,则缩进十二的空格宽度
  175.             }else if(temp.matches("^\\t{4}\\S.*$")){
  176.                 sb.append(TABWIDTH+TABWIDTH+TABWIDTH+TABWIDTH+temp.trim()+"\n");        //如果行开头有四个空白字符,则缩进十六的空格宽度
  177.             }else if(temp.matches("^\\t{5}\\S.*$")){
  178.                 sb.append(TABWIDTH+TABWIDTH+TABWIDTH+TABWIDTH+TABWIDTH+temp.trim()+"\n");       //如果行开头有五个空白字符,则缩进二十个空格宽度
  179.             }else{
  180.                 sb.append(temp.trim()+"\n");
  181.             }
  182.         }
  183.         sb.append("</p>");
  184.  
  185.         outputFileName.append(sourceFile.getPath().substring(0,sourceFile.getPath().toLowerCase().lastIndexOf(".txt"))).append(".html");        //将原文本文件的名字中的“.txt”后缀去掉,换成“.html”
  186.         File outputFile = new File(outputFileName.toString());
  187.         if(outputFile.exists())
  188.             outputFile.delete();
  189.         PrintWriter pw = new PrintWriter(outputFile,encoder);
  190.         pw.println(sb.toString());
  191.         pw.flush();
  192.         pw.close();
  193.         System.out.println("Transformation succeed !");
  194.     }
  195.  
  196.     //文章中的“忽略标记”可能有两种形式:其一直接就是配置文件中定义的格式,如“<coolcode>”;
  197.     //也可以是有属性的,如“<coolcode lang="java" download="sample.java">”。
  198.     //两种格式通过本方法一律还原成配置文件中的格式,然后返回。
  199.     private String peeler(String escapeMarker){
  200.         int i = escapeMarker.trim().indexOf(" ");
  201.         if(i> 0){
  202.             if(escapeMarker.trim().matches("^\\<.*$"))
  203.                 return escapeMarker.trim().substring(0,i)+">";
  204.             if(escapeMarker.trim().matches("^\\[.*$"))
  205.                 return escapeMarker.trim().substring(0,i)+"]";
  206.         }
  207.         return escapeMarker.trim();
  208.     }
  209.  
  210.     //从配置文件中读取“忽略标记”到escapeMarkers集合中
  211.     private int readEscapeMarkers() throws IOException{
  212.         escapeMarkers = new HashSet();
  213.         Properties ppt = new Properties();
  214.         File initFile = new File("EscapeMarkers.properties");
  215.         if(!initFile.exists())
  216.             return -1;
  217.         ppt.load(new FileInputStream(initFile));
  218.         Enumeration e = ppt.propertyNames();
  219.         while(e.hasMoreElements()){
  220.             escapeMarkers.add(e.nextElement());
  221.         }
  222.         return escapeMarkers.size();
  223.     }
  224.    
  225.     // 从配置文件读取"忽略标记"的结束标记
  226.     private String readClosingEscapeMarker(String unclosedEscapeMarker) throws IOException{
  227.         Properties ppt = new Properties();
  228.         File initFile = new File("EscapeMarkers.properties");
  229.         if(!initFile.exists())
  230.             return null;
  231.         ppt.load(new FileInputStream(initFile));
  232.         return ppt.getProperty(unclosedEscapeMarker);
  233.     }
  234.  
  235.     //从iconograph.properties文件中读出插图URL
  236.     private String readIconographURL(String arg,String align) throws IOException{
  237.         File iconograph = new File("iconograph.properties");
  238.         if(!iconograph.exists())
  239.             return "";
  240.         Properties ppt = new Properties();
  241.         ppt.load(new FileInputStream(iconograph));
  242.         String url = ppt.getProperty(arg);
  243.         if(url == null)
  244.             return "";
  245.         if(url.trim().matches("^http://.*$"))
  246.             return "<img src=\""+url.trim()+"\" align=\""+align+"\">\n";
  247.         else
  248.             return "";
  249.     }
  250.  
  251.     //将help.txt文件中的内容打印出来
  252.     private void printHelpMsg() throws IOException{
  253.         System.out.println(VERSION);
  254.         File helpFile = new File("help.txt");
  255.         if(!helpFile.exists())
  256.             System.out.println("Help file missing !");
  257.         else{
  258.             BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(helpFile)));
  259.             String temp;
  260.             while((temp=br.readLine())!=null){
  261.                 System.out.println(temp);
  262.             }
  263.         }
  264.         System.exit(0);
  265.     }
  266.  
  267.     //打印版本信息
  268.     private void printVersionMsg(){
  269.         System.out.println("\n"+VERSION);
  270.         System.out.println("\tAuthor : Lenin Lee");
  271.         System.out.println("\t"+DATE);
  272.         System.out.println("\tWebsite : www.leninlee.cn");
  273.         System.exit(0);
  274.     }
  275.    
  276.     public static void main(String[] args) throws IOException
  277.     {
  278.         FormatDraft fd = new FormatDraft();
  279.         fd.readEscapeMarkers();
  280.         if(args.length <1){
  281.             System.out.println("Arguments missing !");
  282.             System.out.println("\tjava FormatDraft -<argument> <filePath>");
  283.             System.out.println("\tOr input \"java FormatDraft -h\" for help .");
  284.             System.exit(0);
  285.         }
  286.         //程序后面直接跟文本文件的路径,把该文本文件当普通格式处理,以UTF-8解码文本文件,以UTF-8编码HTML文件
  287.         if(args.length == 1){
  288.             if(args[0].equals("-v") || args[0].equals("-version"))
  289.                 fd.printVersionMsg();
  290.             else if(args[0].equals("-h") || args[0].equals("-help"))
  291.                 fd.printHelpMsg();
  292.             else fd.transformNormalDraft(args[0],"utf-8","utf-8");
  293.         }
  294.         //命令格式:java FormatDraft -vim /home/lenin/sample.txt
  295.         //默认以UTF-8解码和编码
  296.         if(args.length == 2){
  297.             if(args[0].equals("-vim"))
  298.                 fd.transformVimDraft(args[1],"utf-8","utf-8");
  299.             else if(args[0].equals("-normal"))
  300.                 fd.transformNormalDraft(args[1],"utf-8","utf-8");
  301.             else{
  302.                 System.out.println("Wrong argument !");
  303.                 System.exit(0);
  304.             }
  305.         }
  306.         //命令格式:java FormatDraft -vim gbk /home/lenin/sample.txt
  307.         //第二个参数即解码文本文件时使用的编码方案,默认以utf-8格式编码转换结果
  308.         if(args.length == 3){
  309.             if(args[0].equals("-vim"))
  310.                 fd.transformVimDraft(args[2],args[1],"utf-8");
  311.             else if(args[0].equals("-normal"))
  312.                 fd.transformNormalDraft(args[2],args[1],"utf-8");
  313.             else{
  314.                 System.out.println("Wrong argument !");
  315.                 System.exit(0);
  316.             }
  317.         }
  318.         //命令格式:java FormatDraft -normal gbk utf-8 /home/lenin/sample.txt
  319.         //第二个参数是解码文本文件时使用的编码方案,第三个参数是编码转换结果时用的编码方案
  320.         if(args.length == 4){
  321.             if(args[0].equals("-vim"))
  322.                 fd.transformVimDraft(args[3],args[2],args[1]);
  323.             else if(args[0].equals("-normal"))
  324.                 fd.transformNormalDraft(args[3],args[2],args[1]);
  325.             else{
  326.                 System.out.println("Wrong argument !");
  327.                 System.exit(0);
  328.             }
  329.         }
  330.         if(args.length> 4){
  331.             System.out.println("Wrong argument !");
  332.             System.exit(0);
  333.         }
  334.     }
  335. }

    下面即为“忽略标记”在配置文件"EscapeMarkers.properties"中的定义格式:

HTML:
  1. <coolcode>=</coolcode>
  2. [java]=[/java]
  3. [cpp]=[/cpp]
  4. [c]=[/c]
  5. [python]=[/python]
  6. [php]=[/php]

    下面是文章插图地址信息在配置文件"iconograph.properties"中的定义格式:

HTML:
  1. vim=http://sinolog.it/images/the-mug-of-vi.jpg
  2. normal=http://photo15.yupoo.com/20071222/205558_725945963_m.jpg

Tags :

随机日志

來留言吧!


Please copy the string ittOZ6 to the field below:

*
To prove you're a person (not a spam script), type the security word shown in the picture. Click on the picture to hear an audio file of the word.
Click to hear an audio file of the anti-spam word

尚未有留言

[...] 后来我写了一个转换普通文本到HTML文档的程序,只要使用特定的格式写文章,然后使用该程序就可以自动在相应位置插入适当的HTML标记。这简化了工作量,我终于可以只关心文章内容而又不用担心文章的排版了。但是,该程序只能识别几种简单的布局格式,如果要在文章中加入其它的元素,就必须加入新的布局并在程序中加入新的代码段…… [...]

 

留言板RSS 引用 URI

來留言吧!

«
»