`
RednaxelaFX
  • 浏览: 3053134 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

将ANTLR生成的.tokens文件重格式化(C#版)(20080721更新)

    博客分类:
  • C#
阅读更多
相关链接:
将ANTLR生成的.tokens文件重格式化(Ruby版)
将ANTLR生成的.tokens文件重格式化(C++版)

既然都把Ruby和C++版写出来了,干脆有爱的把C#的版本也实现一个来看看。
果然不出所料,代码长度介于C++与Ruby的版本之间。比起C++版来说最大的优势就是不用自己去关心内存管理的问题。这个应用场景并不会很让人在意内存的使用量就是了。

其实这代码的长度跟Ruby的相比长不了多少,只不过我的这个实现里定义了一个struct用了好几行。如果像Ruby版本一样使用正则表达式的话就不用定义这个struct了。
注意到排序的部分,使用lambda expression的C#代码完全可以跟Ruby代码的简洁性媲美:一句话解决,甚至连类型都不需要指定,编译器会推导出来。因为用到的lambda表达式里只有一个表达式,所以连包围它的花括号都可以省下,真好嗯 = =
不需要像C++里那样为了使用自定义functor而需要专门定义一下给个名字……

嗯,我对C#的熟悉程度比对Ruby稍微高一些,写代码的速度目前还是C#快点。不过我相信只要不是为了追求执行效率的话,过不了多久我写Ruby代码的速度就会超过写C#的速度了。Sigh,动态语言的好处。

reformat.cs: (without regex)
using System;
using System.IO;
using System.Collections.Generic;

struct TokenNameValuePair {
    private string m_name;
    private int    m_value;
    
    public TokenNameValuePair(string name, int value) {
        this.m_name  = name;
        this.m_value = value;
    }
    
    public string Name  { get { return this.m_name; } }
    public int    Value { get { return this.m_value; } }
}

sealed class ReformatTokensFile {
    private const string USAGE = "Usage: reformat [tokens file] [output file]";
    
    static void Reformat(string infilename, string outfilename) {
        List<TokenNameValuePair> lines = new List<TokenNameValuePair>();
        
        using (StreamReader reader = File.OpenText(infilename)) {
            string line = reader.ReadLine();
            while (null != line) {
                string[] parts = line.Split('=');
                string   name  = parts[0];
                int      value = Convert.ToInt32(parts[1]);
                lines.Add(new TokenNameValuePair(name, value));
                
                line = reader.ReadLine();
            }
        }
        
        lines.Sort((first, second) => first.Value.CompareTo(second.Value));
        
        using (StreamWriter writer = File.CreateText(outfilename)) {
            foreach (TokenNameValuePair pair in lines) {
                writer.WriteLine("{0}={1}", pair.Value, pair.Name);
            }
        }
    }
    
    static void Main(string[] args) {
        if (2 != args.Length) {
            Console.WriteLine(USAGE);
            return;
        }
        
        string infilename  = args[0];
        string outfilename = args[1];
        if (!File.Exists(infilename)) {
            Console.WriteLine("Invalid input file name.");
            Console.WriteLine(USAGE);
            return;
        }
        
        Reformat(infilename, outfilename);
    }
}

注意第36行的lambda expression。
说起来,非要“减少行数”的话,在读文件的那个using语句(25-33行)里改成这样也行:
foreach (string line in reader.ReadToEnd().Split('\n')) {
    if (line.Equals(string.Empty)) continue;
    string[] parts = line.Split('=');
    lines.Add(new TokenNameValuePair(parts[0], Convert.ToInt32(parts[1])));
}

行数减少了,性能上没什么显著的好处——一次把整个文件都读进来了,要是内存小而文件大的话恐怕要吃不消 = =

来看看用正则表达式的版本,行数是不是显著减少了
reformat.cs: (with regex)
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;

sealed class ReformatTokensFile {
    private const string USAGE = "Usage: reformat [tokens file] [output file]";
    
    static void Reformat(string infilename, string outfilename) {
        List<string> lines  = new List<string>();
        Regex revert = new Regex(@"^([^=]+)=([0-9]+)$");
        
        using (StreamReader reader = File.OpenText(infilename)) {
            string line = reader.ReadLine();
            while (null != line) {
                lines.Add(revert.Replace(line, "$2=$1"));
                line = reader.ReadLine();
            }
        }

        Regex leadingNumber = new Regex(@"^[0-9]+");
        lines.Sort((first, second) =>
            Convert.ToInt32(leadingNumber.Match(first).Value).CompareTo(
            Convert.ToInt32(leadingNumber.Match(second).Value))
        );
        
        using (StreamWriter writer = File.CreateText(outfilename)) {
            foreach (string line in lines) {
                writer.WriteLine(line);
            }
        }
    }
    
    static void Main(string[] args) {
        if (2 != args.Length) {
            Console.WriteLine(USAGE);
            return;
        }
        
        string infilename  = args[0];
        string outfilename = args[1];
        if (!File.Exists(infilename)) {
            Console.WriteLine("Invalid input file name.");
            Console.WriteLine(USAGE);
            return;
        }
        
        Reformat(infilename, outfilename);
    }
}

注意第22-25行的lambda expression。

当然必须要提到的是,像上面这样用正则表达式很浪费时间。Ruby版的时候我纯粹是为了写起来顺手才那样写的,然而如果是用C#的话多少还是应该考虑下运行效率吧?
使用正则表达式来匹配,这个动作本身就会产生不少临时string对象,所以在不需要使用捕获型括号的时候应该尽量避免使用括号。
把第12行定义的leadingNumber这个正则表达式放到lambda表达式中传给Sort(),意味著排序中的每次比较都必须做两次正则表达式匹配。数组稍微大一点的话这里消耗的时间就会变得可观了。仔细想想,相对来说我还是比较倾向不在这里(C#版)中使用正则表达式,因为这个场景里Split()已经够用。
(更新:于是在Ruby的版本里我也避免了在sort的时候用正则表达式匹配)

-- LINQ更新:
不过前面的代码都还没充分发挥出C# 3.0的能力。下面就用C# 3.0的隐式类型、LINQ、var等新特性来重写第一个版本的代码:
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;

sealed class ReformatTokensFile {
    private const string USAGE = "Usage: reformat [tokens file] [output file]";
    
    static void Reformat(string infilename, string outfilename) {
        var lines = MakeList(new { Name = string.Empty, Value = 0 });
        
        using (var reader = File.OpenText(infilename)) {
            var line = reader.ReadLine();
            while (null != line) {
                var parts = line.Split('=');
                var name  = parts[0];
                var value = Convert.ToInt32(parts[1]);
                lines.Add(new { Name = name, Value = value });
                
                line = reader.ReadLine();
            }
        }
        
        using (var writer = File.CreateText(outfilename)) {
            foreach (var pair in from l in lines
                                 orderby l.Value
                                 select l)
                writer.WriteLine("{0}={1}", pair.Value, pair.Name);
        }
    }

    public static List<T> MakeList<T>(T itemOftype) {
        return new List<T>();
    }
    
    static void Main(string[] args) {
        if (2 != args.Length) {
            Console.WriteLine(USAGE);
            return;
        }
        
        var infilename  = args[0];
        var outfilename = args[1];
        if (!File.Exists(infilename)) {
            Console.WriteLine("Invalid input file name.");
            Console.WriteLine(USAGE);
            return;
        }
        
        Reformat(infilename, outfilename);
    }
}

要说简洁了的话,确实啊……

注意这段代码是如何使用MakeList<T>这个辅助方法来创建隐式类型的List<T>的。
这种使用泛型的方式,在Java中就做不到(也没必要就是了,反正Java的泛型是类型擦除)。
2
1
分享到:
评论
1 楼 RednaxelaFX 2008-07-21  
(更新)
最近用LINQ比较多,顺便把这个例子也更新个使用LINQ的版本。
另外,关于C# 3.0的自动生成的属性也放了例子在另一帖:C# 3.0的自动生成属性

相关推荐

    开源项目-antlr-antlr4.zip

    ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR4是它的最新版本,它提供了显著的性能提升和许多新特性。这个开源项目“antlr-...

    antlr-3.3.tar.gz_antlr_antlr-2.7.5.tar.gz

    ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR被广泛应用于各种语言和系统的开发,包括编程语言、配置文件、通信协议等。这...

    antlr_2.7.6.jar.zip

    ANTLR( Anatomy of a Little Language Translator)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR主要用于构建语言、工具和框架。从简单的命令行工具到复杂的编程语言,...

    antlr.jar和json.jar

    1. **词法分析**:ANTLR首先将输入源代码转换为词法单元(tokens),这些单元是由词法规则定义的最小语法单元。 2. **语法分析**:然后ANTLR使用语法分析器对词法单元进行解析,生成抽象语法树(AST)。这一步骤...

    antlr-v4jar包和使用教程

    ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR被广泛应用于各种语言和系统的开发,包括数据库查询解析、配置文件处理、编程...

    antlr-runtime-3.1.3.jar.zip

    ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。ANTLR被广泛应用于构建语言、工具和框架,如SQL查询解析器、XML处理器、配置文件...

    antlr2peg:从ANTLR .g4文件到PEG语法的幼稚翻译器

    这些文件包含了词法符号(tokens)和语法规则,允许ANTLR生成对应目标语言的解析器和词法分析器。 PEG(Parsers Expression Grammar)是一种上下文无关的语法表示法,全称为解析表达式语法。与传统的LL或LR解析器...

    Antlr简介及中文手册

    - options域:设置ANTLR生成器的选项,如目标语言、编码等。 - lexer和parser规则:定义词法规则和语法规则,每个规则代表一种语言结构。 - parser规则通常用于构建语法树,lexer规则用于生成Token。 - tokens域:可...

    antlr在.NET环境下的Runtime

    这个库提供了必要的类和方法,使得在C#或其他.NET语言中能够调用ANTLR生成的解析器和词法分析器。 使用ANTLR在.NET环境下进行开发,首先需要安装ANTLR的.NET版本,这通常包括ANTLR的生成器工具和运行时库。生成器...

    antlr4-json-parser.zip

    ANTLR4(ANother Tool for Language Recognition)是一款强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。它广泛应用于构建语言、工具和框架,如JSON解析器。ANTLR4可以生成Java、C#、...

    利用Antlr实现的命令行解析器

    最后,`FCmd.tokens`是一个生成的文件,它包含了Antlr为识别的标记分配的唯一整数值。这些值在内部用于快速比较和处理标记。 总的来说,通过Antlr,我们可以高效地解析特定语法格式的命令行,实现自定义的命令行...

    antlr-intellij-plugin-v4-1.18-2020.zip

    ANTLR生成Java、C#、Python、JavaScript等语言的目标代码,支持LL(*)和LR(1)语法分析,提供了一个强大的错误检测系统。 IntelliJ IDEA是一款非常受欢迎的Java集成开发环境(IDE),它提供了丰富的功能来提升开发者...

    smt-antlr4-java-parser:该项目在构建时将生成 ANTLR4 Java 解析类

    ANTLR(ANother Tool for Language Recognition)是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。它广泛应用于构建语言、工具和框架,包括SQL解析器、XML处理器以及Java、C#和Python...

    smt-antlr4-javascript-parser:该项目在构建时将生成 ANTLR4 JavaScript 解析类

    ANTLR4 是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。它广泛应用于各种编程语言的语法解析,包括但不限于Java、C#、Python、JavaScript等。在这个名为“smt-antlr4-javascript-...

    开源的语法分析工具Another Tool for Language Recognition

    4. **配置文件解析**:ANTLR可以用来解析复杂的配置文件格式,比如JSON、XML等。 5. **自然语言处理**:虽然主要应用于编程语言,但ANTLR也可以扩展用于自然语言处理领域。 #### 六、ANTLR的使用 - **安装**: ...

    Tucode:使用 ANTLR4 和 Java-Groovy 的小型编译器项目的存储库

    ANTLR4是一个强大的解析器生成器,广泛用于处理各种语言和格式,包括编程语言、查询语言、配置文件等。这个项目提供了一个很好的实践平台,帮助我们了解编译器设计的基本概念和技术。 首先,ANTLR4(ANother Tool ...

    c语法分析器

    6. `c.grm`: 这可能是描述C语言语法的语法文件,可能使用类似YACC或ANTLR的格式。语法文件定义了C语言的语法规则,用于指导解析过程。 7. `c_parser.h`, `c_main.h`, `c_lexer.h`: 这些是头文件,包含了对应源文件...

    PracticaObligatoria

    在IT行业中,ANTLR是一个强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件。这个“PracticaObligatoria”项目很可能是一个基于ANTLR的编程实践作业,旨在帮助学生深入理解ANTLR的工作原理及其...

    词法语法分析器(JAVA)

    通过词法和语法分析,开发者可以实现自定义的代码检查、格式化和转换功能。 6. **自定义解析规则**: 开发者可以根据需求定制词法和语法规则,例如,创建一个新的DSL(Domain Specific Language)或者解析非标准...

Global site tag (gtag.js) - Google Analytics