2016-11-13 4 views
0

私はテキストファイルを解析し、後で処理するためにそれをトークン化しています。プログラムは、File.ReadAllText()を使用してテキストファイル全体をメモリに読み込んだ後、文字列全体をトークナイザに渡します。トークナイザは、テキストをStringReaderに配置し、一度に1文字ずつ処理します。一度に1つの文字を解析するときにStringReader内の改行文字を認識

現在は、不一致が発生するたびに基本的な構文エラーが生成されますが、エラーが発生した行番号を含めることにします。 StringReaderを使用してchar-by-char文字列を処理するときに、\r\nシーケンスを認識できますか?なぜなら、明示的に '\ r'と '\ n'の両方を探すために私のcase文にチェックを入れていて、どちらのブランチもデバッグ中に起動しないからです。それ以外の文字はすべて一致します。

コード例:(フルコンテキストで簡略化されたバージョンについては下記を参照)

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は2つの文字を含む文字列なので、ここではうまくいきません。なぜなら、私はPeek先に1文字だけ先送りしているからです。答えがdefaultケースのifの文に2文字のPeekを含めるのでなければ、この状況を捕まえることができますか?

行末の文字を認識する方法が必要なようです。私は何が欠けていますか?ありがとう。スティーブに対応して

編集私はトークナイザを簡素化:

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がコンソールに出力され、デバッグ中にヒットしません。実際、コンソールに書き込まれる唯一の出力は「Done!」というテキストです。

+0

ピークはストリームを次の文字に進めません。改行に達するまでcharで進めるようにストリームの位置をどのようにインクリメントしますか? – Steve

+0

ところで、私はあなたの問題を再現できません。あなたはすべてのスイッチケースをコメントアウトしようとしましたが、改行に関連する2つのものと、デバッガで何が起こるかを見ましたか? – Steve

+0

@スティーブこれは大きなメソッド内のスニペットです。ストリームは 'Parse * Token'メソッド呼び出しで適切な数の' Read() '呼び出しによって進められます。だから、 'ParseCommaToken'は' Read() 'を一度呼び出すのに対し、' ParseStringToken'は閉じダブルクォートに達するまで 'Read()'を行います。希望は意味をなさない。 – Dave

答えて

0

誰かがこの特定の問題に遭遇した場合(自分で質問する前に特定の回答を見つけることができなかったので)、Char.IsWhitespace()への呼び出しは、行末文字\rおよび\n。私は愚かにこれを忘れていた。

私は空白をバイパスする必要があるので、問題を解決するためにこれらの文字をキャッチしたいと思っています。私は自分自身のプライベートメソッドを作成し、その2つの文字のいずれかにfalseを返します。トークナイザのループ内

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

私はChar.IsWhitespaceへの呼び出しに置き換え:

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

をそして今、それは別に、これらのEOL文字の両方で正常に起動します。したがって、EOLシーケンスの解析関数の作成は簡単です。\rが検出されると、それは次のチャート\nを消費し、EndOfLineTokenを正しく出力します。

+0

ReadAllTextの代わりにFile.ReadLinesを使用することを提案できますか?これは、各行が配列内の位置によって識別されるため、最初は問題を回避します。文字列から直接charを使ってループを変更するだけでよい – Steve

関連する問題