在一次解析一个char时,识别StringReader中的换行符

时间:2016-11-13 20:57:31

标签: c# parsing tokenize

我正在解析文本文件并对其进行标记以供以后处理。程序使用File.ReadAllText()将整个文本文件读入内存,然后将整个字符串传递给tokenizer。标记器将文本放入StringReader,然后一次处理一个字符。

现在它会在发生不匹配时生成基本语法错误,但我想在错误发生的位置包含行号。使用\r\n处理字符串char-by-char时是否可以识别StringReader序列?因为我在我的case语句中包含检查以显式查找'\ r'和'\ n',并且在调试期间都不会触发任何分支。除了这些之外,所有其他字符都匹配。

示例代码:(有关完整上下文的简化版本见下文)

var c = (char)_reader.Peek();
switch(c)
{
    ... bunch of case statements here ...
    case '"':
        ParseStringToken();
        break;
    case ',':
        ParseCommaToken();
        break;
    case '.':
        ParseFullStopToken();
        break;
    case '\r':
        ParseEndOfLineToken();
        break;
    case '\n':
        ParseEndOfLineToken();
        break;
    ... more case statements ...
}

示例中的最后一个分支永远不会触发。我也尝试过识别\n,它也永远不会发射。由于Environment.NewLine是一个包含两个字符的字符串,看起来不像在这里工作,因为我前面只有Peek一个字符。除非答案是在Peek案例中的if语句中包含两个字符default,以便了解这种情况?

似乎必须有一种方法来识别行尾字符。我错过了什么?感谢。

编辑为了回应Steve,我简化了标记器:

public class Tokenizer
{
    private readonly StringReader _reader;
    private List<Token> _tokens;

    public Tokenizer(string text)
    {
        _reader = new StringReader(text);
        _tokens = new List<Token>();
    }

    public IEnumerable<Token> Tokenize()
    {
        while (_reader.Peek() > -1)
        {
            while (Char.IsWhiteSpace((char)_reader.Peek()))
                _reader.Read();

            if (-1 == _reader.Peek())
                break;

            var c = (char)_reader.Peek();
            switch(c)
            {
                case '\n':
                    Console.WriteLine("slash-n");
                    _reader.Read();
                    break;
                case '\r':
                    Console.WriteLine("slash-r");
                    _reader.Read();
                    break;
                default:
                    _reader.Read();
                    break;
            }
        }
        return _tokens;
    }
}

这是调用代码,它现在只是一个控制台应用程序的Main方法:

static void Main(string[] args)
{
    var path = @"source.txt";

    var text = File.ReadAllText(path);

    var tokenizer = new Tokenizer(text);
    var tokens = tokenizer.Tokenize();
    Console.WriteLine(String.Join("\n", tokens));
    Console.WriteLine();
    Console.WriteLine("Done!");
    Console.ReadKey();
}

slash-rslash-n都不会输出到控制台,也不会在调试过程中被点击。事实上,这里写入控制台的唯一输出是文本“完成!”。

1 个答案:

答案 0 :(得分:0)

如果有其他人遇到这个特定的问题(因为在我自己提出问题之前我找不到关于它的特定答案),结果发现对Char.IsWhitespace()的调用对于两个结尾都返回true行字符\r\n。我愚蠢地忘记了这一点。

由于我需要绕过空格但想要捕获这些字符,为了解决这个问题,我只是创建了自己的私有方法来包装它并为这两个字符中的任何一个返回false。

private bool IsWhitespace(char c)
{
    return ('\n' != c && '\r' != c && Char.IsWhiteSpace(c));
}

在tokenizer的循环中,我将调用替换为Char.IsWhitespace

while (IsWhitespace((char)_reader.Peek()))
    _reader.Read();

现在它分别在这两个EOL字符上正确激活。因此,为EOL序列编写一个解析函数是微不足道的,一旦检测到\r,它就会消耗它和下一个char \n并正确发出EndOfLineToken