C# I/O 助手类

在使用 C# 语言解 ACM 题的时候,如果能够有一个 ReadInt32 方法直接从标准输入读取整数比较方便的。下面就是一个 I/O 助手类 IOHelper:

namespace Skyiv
{
  using System;
  using System.IO;
  using System.Text;

  sealed class IOHelper : IDisposable
  {
    static readonly Encoding Encoding = Encoding.ASCII; // or UTF8 ?
    static readonly byte[] EOLS = Encoding.GetBytes(Environment.NewLine);
    static readonly byte[] BLANKS = { 9, 10, 13, 32 }; // tab,lf,cr,space
    static readonly byte EOF = 0; // assume '\0' not in input file
    
    byte[] buf = new byte[32]; // for Write(int n)
    byte[] buffer = new byte[64 * 1024];
    int current = 0;
    int count = 0;
    BinaryReader reader;
    BinaryWriter writer;
    
    public IOHelper()
      : this(Console.OpenStandardInput(), Console.OpenStandardOutput())
    {}
    
    public IOHelper(Stream reader, Stream writer)
    {
      this.reader = new BinaryReader(reader);
      this.writer = new BinaryWriter(writer);
    }
    
    byte ReadByte()
    {
      if (current >= count)
      {
        count = reader.Read(buffer, current = 0, buffer.Length);
        if (count == 0) return EOF;
      }
      return buffer[current++];
    }
    
    public static byte[] GetBytes(string str)
    {
      return Encoding.GetBytes(str);
    }
    
    public int ReadInt32()
    {
      var n = 0;
      var ok = false;
      for (byte b; (b = ReadByte()) != EOF; )
      {
        if (Array.IndexOf(BLANKS, b) >= 0)
          if (ok) break;
          else continue;
        n = n * 10 + (b - '0');
        ok = true;
      }
      return n;
    }
    
    public int ReadLine(byte[] buffer)
    {
      var n = 0;
      while (n < buffer.Length)
      {
        var b = ReadByte();
        if (b == EOLS[0])
        {
          if (EOLS.Length == 2 && ReadByte() != EOLS[1])
            throw new InvalidDataException("Invalid EOL");
          break;
        }
        buffer[n++] = b;
      }
      return n;
    }

    public void Write(int n)
    {
      if (n == 0) { writer.Write((byte)'0'); return; }
      var i = buf.Length;
      for (; n > 0; n /= 10) buf[--i] = (byte)((n % 10) + '0');
      Write(buf, i, buf.Length - i);
    }
    
    public void Write(byte[] buffer, int index, int count)
    {
      writer.Write(buffer, index, count);
    }
    
    public void Write(string str)
    {
      var buffer = Encoding.GetBytes(str);
      writer.Write(buffer, 0, buffer.Length);
    }
    
    public void WriteSpace()
    {
      writer.Write(BLANKS, 3, 1);
    }
    
    public void WriteLine()
    {
      writer.Write(EOLS, 0, EOLS.Length);
    }
    
    public void Dispose()
    {
      if (reader != null) reader.Close();
      if (writer != null) writer.Close();
    }
  }
}

此外,IOHelper 类还提供以下方法用于处理字符串:

  • public int ReadLine(byte[] buffer)
  • public void Write(byte[] buffer, int index, int count)

类似于 C/C++ 语言,这两个方法仅仅把字符串当作字节数组处理,使用 ASCII 码。而不是象 C# 语言中字符串是使用 Unicode 进行编码。

下面是一个使用示例,题目来源请参见“I-Keyboard”这篇随笔。

namespace Skyiv.Ben.Acm
{
  using System;

  // http://www.spoj.pl/problems/IKEYB/
  sealed class Ikeyb
  {
    const int MAX = 90;
    
    static int[,] cost = new int[MAX + 1, MAX + 1];
    static int[,] price = new int[MAX + 1, MAX + 1];
    static int[,] index = new int[MAX + 1, MAX + 1];
    static int[] F = new int[MAX];
    static byte[] keys = new byte[MAX];
    static byte[] letters = new byte[MAX];
    static byte[] message1 = IOHelper.GetBytes("Keypad #");
    static byte[] message2 = IOHelper.GetBytes(": ");
    
    static IOHelper helper;

    static void Main()
    {
      using (helper = new IOHelper())
      {
        var runner = new Ikeyb();
        var T = helper.ReadInt32();
        for (var n = 1; n <= T; n++) runner.Run(n);
      }
    }

    void Run(int n)
    {
      var K = helper.ReadInt32();
      var L = helper.ReadInt32();
      helper.ReadLine(keys);
      helper.ReadLine(letters);
      for (var i = 0; i < L; i++) F[i] = helper.ReadInt32();
      Initialize(K, L);
      Compute(K, L);
      helper.Write(message1, 0, message1.Length);
      helper.Write(n);
      helper.Write(message2, 0, 1);
      helper.WriteLine();
      Output(K, L);
      helper.WriteLine();
    }
    
    void Initialize(int K, int L)
    {
      for (var i = 0; i <= K; i++)
        for (var j = 1; j <= L; j++)
          price[i, j] = int.MaxValue / 2;
      for (var i = 1; i <= L; i++)
        for (var j = i; j <= L; j++)
          cost[i, j] = cost[i, j - 1] + (j - i + 1) * F[j - 1];
    }
    
    void Compute(int K, int L)
    {
      for (var i = 1; i <= K; i++)
        for (var j = i; j <= L; j++)
          for (var n = 1; n <= j - i + 1; n++)
          {
            var sum = price[i - 1, j - n] + cost[j - n + 1, j];
            if (sum <= price[i, j])
            {
              price[i, j] = sum;
              index[i, j] = n;
            }
          }
    }

    void Output(int K, int L)
    {
      if (K == 0) return;
      var n = index[K--, L];
      Output(K, L - n);
      helper.Write(keys, K, 1);
      helper.Write(message2, 0, message2.Length);
      helper.Write(letters, L - n, n);
      helper.WriteLine();
    }
  }
}

如果给出以下输入:

1
2 5
*#
ABCDE
1024
32768
2147483647
987
654321

上述程序将产生以下输出:

Keypad #1:
*: ABCD
#: E

注意上述输出其实是有问题的,正确的输出应该分为 AB 和 CDE 两组。但是程序本身是没有问题的,而是输入数据有问题。因为原来的题目中限定各个字母出现的频率不能超过 100000。

你可能感兴趣的:(C# I/O 助手类)