1

我正在寻找快速类来处理文本文件并舒适地阅读不同的对象(NextInt32、NextDouble、NextLine 等方法)。你能给我一些建议吗?

编辑:在我的情况下,BinaryReader 是坏类。我的数据格式不是二进制的。我有文件

1 2 3
FirstToken NextToken
1.23 2,34

我想用如下代码读取这个文件:

int a = FileReader.NextInt32();
int b = FileReader.NextInt32();
int c = FileReader.NextInt32();
int d = FileReader.NextString();
int e = FileReader.NextString();
int f = FileReader.NextDouble();
int g = FileReader.NextDouble();

Edit2:我正在寻找来自 Java 的模拟扫描仪

4

7 回答 7

5

我相信这种扩展方法TextReader可以解决问题:

public static class TextReaderTokenizer
{
    // Adjust as needed. -1 is EOF.
    private static int[] whitespace = { -1, ' ', '\r' , '\n', '\t' };

    public static T ReadToken<T>(this TextReader reader)
    {
        StringBuilder sb = new StringBuilder();
        while (Array.IndexOf(whitespace, reader.Peek()) < 0)
        {
            sb.Append((char)reader.Read());
        }
        return (T)Convert.ChangeType(sb.ToString(), typeof(T));
    }    
}

它可以这样使用:

TextReader reader = File.OpenText("foo.txt");
int n = reader.ReadToken<int>();
string s = reader.ReadToken<string>();

[编辑]根据问题评论的要求,这是上面的实例包装器版本,它使用定界符和 参数化CultureInfo

public class TextTokenizer
{
    private TextReader reader;
    private Predicate<char> isDelim;
    private CultureInfo cultureInfo;

    public TextTokenizer(TextReader reader, Predicate<char> isDelim, CultureInfo cultureInfo)
    {
        this.reader = reader;
        this.isDelim = isDelim;
        this.cultureInfo = cultureInfo;
    }

    public TextTokenizer(TextReader reader, char[] delims, CultureInfo cultureInfo)
    {
        this.reader = reader;
        this.isDelim = c => Array.IndexOf(delims, c) >= 0;
        this.cultureInfo = cultureInfo;
    }

    public TextReader BaseReader
    {
        get { return reader; }
    }

    public T ReadToken<T>()
    {
        StringBuilder sb = new StringBuilder();
        while (true)
        {
            int c = reader.Peek();
            if (c < 0 || isDelim((char)c))
            {
                break;
            }
            sb.Append((char)reader.Read());
        }
        return (T)Convert.ChangeType(sb.ToString(), typeof(T));
    }    
}

示例用法:

TextReader reader = File.OpenText("foo.txt");
TextTokenizer tokenizer = new TextTokenizer(
    reader,
    new[] { ' ', '\r', '\n', '\t' },
    CultureInfo.InvariantCulture);
int n = tokenizer.ReadToken<int>();
string s = tokenizer.ReadToken<string>();
于 2009-10-13T19:13:59.507 回答
4

您应该准确定义文件格式的外观。你将如何表示一个带有空格的字符串?是什么决定了线路终结器的去向?

一般来说,您可以使用TextReader和它的ReadLine方法,然后是double.TryParseint.TryParse- 但您需要首先确定格式。

于 2009-10-13T18:40:14.600 回答
4

我将把它作为一个单独的答案添加,因为它与我已经给出的答案完全不同。以下是您可以开始创建自己的 Scanner 类的方法:

class Scanner : System.IO.StringReader
{
  string currentWord;

  public Scanner(string source) : base(source)
  {
     readNextWord();
  }

  private void ReadNextWord()
  {
     System.Text.StringBuilder sb = new StringBuilder();
     char nextChar;
     int next;
     do
     {
        next = this.Read();
        if (next < 0)
           break;
        nextChar = (char)next;
        if (char.IsWhiteSpace(nextChar))
           break;
        sb.Append(nextChar);
     } while (true);
     while((this.Peek() >= 0) && (char.IsWhiteSpace((char)this.Peek())))
        this.Read();
     if (sb.Length > 0)
        currentWord = sb.ToString();
     else
        currentWord = null;
  }

  public bool HasNextInt()
  {
     if (currentWord == null)
        return false;
     int dummy;
     return int.TryParse(currentWord, out dummy);
  }

  public int NextInt()
  {
     try
     {
        return int.Parse(currentWord);
     }
     finally
     {
        readNextWord();
     }
  }

  public bool HasNextDouble()
  {
     if (currentWord == null)
        return false;
     double dummy;
     return double.TryParse(currentWord, out dummy);
  }

  public double NextDouble()
  {
     try
     {
        return double.Parse(currentWord);
     }
     finally
     {
        readNextWord();
     }
  }

  public bool HasNext()
  {
     return currentWord != null;
  }
}
于 2009-10-13T19:04:25.330 回答
2

你检查过 BinaryReader 类吗?是的,它是一个文本文件,但没有什么能阻止您将其视为二进制数据并因此使用 BinaryReader。它具有您正在寻找的所有方法,但 ReadLine 除外。但是,在 BinaryReader 之上实现该方法并不难。

于 2009-10-13T18:18:56.037 回答
1

如果您确实需要文本文件(即 UTF-8 或 ASCII 编码),那么二进制编写器将无法工作。

您可以使用TextReader,但与BinaryReader和不同的是,TextWriter它不支持除 Line 和 之外的任何类型char。您必须定义允许使用的分隔符并自己解析 Line 基础数据。

于 2009-10-13T18:36:47.793 回答
0

System.IO.BinaryReader是您所需要的。

ReadLine方法的实现示例:

public static class Extensions
{
    public static String ReadLine(this BinaryReader binaryReader)
    {
        var bytes = new List<Byte>();
        byte temp;

        while ((temp = (byte)binaryReader.Read()) < 10)
            bytes.Add(temp);

        return Encoding.Default.GetString(bytes.ToArray());
    }
}

使用此类的示例:

using System;
using System.IO;
using System.Security.Permissions;

class Test
{
    static void Main()
    {
        // Load application settings.
        AppSettings appSettings = new AppSettings();
        Console.WriteLine("App settings:\nAspect Ratio: {0}, " +
            "Lookup directory: {1},\nAuto save time: {2} minutes, " +
            "Show status bar: {3}\n",
            new Object[4]{appSettings.AspectRatio.ToString(),
            appSettings.LookupDir, appSettings.AutoSaveTime.ToString(),
            appSettings.ShowStatusBar.ToString()});

        // Change the settings.
        appSettings.AspectRatio   = 1.250F;
        appSettings.LookupDir     = @"C:\Temp";
        appSettings.AutoSaveTime  = 10;
        appSettings.ShowStatusBar = true;

        // Save the new settings.
        appSettings.Close();
    }
}

// Store and retrieve application settings.
class AppSettings
{
    const string fileName = "AppSettings#@@#.dat";
    float  aspectRatio;
    string lookupDir;
    int    autoSaveTime;
    bool   showStatusBar;

    public float AspectRatio
    {
        get{ return aspectRatio; }
        set{ aspectRatio = value; }
    }

    public string LookupDir
    {
        get{ return lookupDir; }
        set{ lookupDir = value; }
    }

    public int AutoSaveTime
    {
        get{ return autoSaveTime; }
        set{ autoSaveTime = value; }
    }

    public bool ShowStatusBar
    {
        get{ return showStatusBar; }
        set{ showStatusBar = value; }
    }

    public AppSettings()
    {
        // Create default application settings.
        aspectRatio   = 1.3333F;
        lookupDir     = @"C:\AppDirectory";
        autoSaveTime  = 30;
        showStatusBar = false;

        if(File.Exists(fileName))
        {
            BinaryReader binReader =
                new BinaryReader(File.Open(fileName, FileMode.Open));
            try
            {
                // If the file is not empty,
                // read the application settings.
                // First read 4 bytes into a buffer to
                // determine if the file is empty.
                byte[] testArray = new byte[3];
                int count = binReader.Read(testArray, 0, 3);

                if (count != 0)
                {
                    // Reset the position in the stream to zero.
                    binReader.BaseStream.Seek(0, SeekOrigin.Begin);

                    aspectRatio   = binReader.ReadSingle();
                    lookupDir     = binReader.ReadString();
                    autoSaveTime  = binReader.ReadInt32();
                    showStatusBar = binReader.ReadBoolean();
                }
            }

            // If the end of the stream is reached before reading
            // the four data values, ignore the error and use the
            // default settings for the remaining values.
            catch(EndOfStreamException e)
            {
                Console.WriteLine("{0} caught and ignored. " +
                    "Using default values.", e.GetType().Name);
            }
            finally
            {
                binReader.Close();
            }
        }

    }

    // Create a file and store the application settings.
    public void Close()
    {
        using(BinaryWriter binWriter =
            new BinaryWriter(File.Open(fileName, FileMode.Create)))
        {
            binWriter.Write(aspectRatio);
            binWriter.Write(lookupDir);
            binWriter.Write(autoSaveTime);
            binWriter.Write(showStatusBar);
        }
    }
}
于 2009-10-13T18:21:11.643 回答
0

您可能可以使用System.IO.File类来读取文件,并使用System.Convert来解析您从文件中读取的字符串。

string line = String.Empty;
while( (line = file.ReadLine()).IsNullOrEmpty() == false )
{
   TYPE value = Convert.ToTYPE( line );
}

TYPE您在该特定行/文件中处理的任何类型在哪里。

如果一行上有多个值,您可以进行拆分并读取各个值,例如

string[] parts = line.Split(' ');
if( parts.Length > 1 )
{
   foreach( string item in parts )
   {
      TYPE value = Convert.ToTYPE( item );
   }
}
else
{
   // Use the code from before
}
于 2009-10-13T18:24:44.170 回答