在C#中从文本文件中读取数字

24

这应该是非常简单的事情。我只想从一个由空格分隔的标记组成的文本文件中读取数字和单词。在C#中怎么做?例如,在C++中,以下代码可用于读取整数、浮点数和单词。我不想使用正则表达式或编写任何特殊的解析代码。

ifstream in("file.txt");
int int_val;
float float_val;
string string_val;
in >> int_val >> float_val >> string_val;
in.close();

此外,每当读取一个记号时,应最多读取一个超出该记号的字符。这使得进一步的文件读取依赖于已读取的记号的值。以具体例子说明,考虑以下内容:

string decider;
int size;
string name;

in >> decider;
if (decider == "name")
    in >> name;
else if (decider == "size")
    in >> size;
else if (!decider.empty() && decider[0] == '#')
    read_remainder_of_line(in);
解析二进制PNM文件也是为什么你希望在读取到完整标记后立即停止读取文件的好例子。
7个回答

29

Brannon的回答解释了如何读取二进制数据。如果你想读取文本数据,你应该读取字符串并进行解析,当然有内置的方法可以使用。

例如,要读取包含数据的文件:

10
10.5
hello

你可以使用:

using (TextReader reader = File.OpenText("test.txt"))
{
    int x = int.Parse(reader.ReadLine());
    double y = double.Parse(reader.ReadLine());
    string z = reader.ReadLine();
}
请注意,此代码没有错误处理。特别是,如果文件不存在、前两行包含不适当的数据或者行数少于两行,则会抛出异常。如果文件只有两行,则在z中留下null值。
要想实现更可靠的解决方案,以便更好地处理失败情况,你需要检查reader.ReadLine()是否返回了null(表示文件结束),并且使用int.TryParsedouble.TryParse代替Parse方法。
这是在假设值之间有行分隔符的情况下。如果你实际上想读取这样的字符串:
10 10.5 hello

那么代码将非常相似:

using (TextReader reader = File.OpenText("test.txt"))
{
    string text = reader.ReadLine();
    string[] bits = text.Split(' ');
    int x = int.Parse(bits[0]);
    double y = double.Parse(bits[1]);
    string z = bits[2];
}

再次提醒,您需要执行适当的错误检测和处理。请注意,如果文件确实只包含单行,则最好使用File.ReadAllText,这使得它稍微简单一些。还有File.ReadAllLines,它将整个文件读取为一个字符串数组。

编辑:如果你需要通过任何空格进行拆分,那么最好使用File.ReadAllText读取整个文件,然后使用正则表达式进行拆分。此时,我想知道如何表示包含空格的字符串。

根据我的经验,通常您对格式了解得更多——是否会有行分隔符,或同一行中通过空格分隔的多个值等等。

我还要补充说,混合二进制/文本格式通常很难处理。简单高效的文本处理通常会读入缓冲区,如果同时有二进制数据,则会变得棘手。如果您需要在二进制文件中包含文本部分,则通常最好包含长度前缀,以便仅解码该数据片段。


这是一个经过深思熟虑的回答,但并不完全符合C++代码的行为,因为它对文件格式做出了太多假设。我想要的只是通过空格分隔标记的分离。所提出的解决方案无法处理例如:10 10.5 helloReadAllText和ReadAllLines也不能按预期运行。以解析二进制PNM文件为例。有一个带有空格分隔标记的头部,后面跟着二进制数据。阅读器应该一次只吃一个标记,并将剩余的文件保持不变。 - Joe
文件格式化未能正常工作。想象一下,10和10.5在同一行上,而令牌“hello”则单独占据一行。 - Joe

10
using (FileStream fs = File.OpenRead("file.txt"))
{
    BinaryReader reader = new BinaryReader(fs);

    int intVal = reader.ReadInt32();
    float floatVal = reader.ReadSingle();
    string stringVal = reader.ReadString();
}

2
这实际上是从二进制文件而不是文本文件中读取。特别是,一个包含“10 10.5 hello”的文件不会被读取为您所期望的内容。虽然这可能与C++的行为相匹配,但我不确定...很可能OP只是误用了“文本文件”这个短语。 - Jon Skeet
我已经发现了这种读取二进制文件的方法。但这不是我想要的。 - Joe
嗯,是的..我误解了。我没有意识到C ++的输入/输出流可以处理从文本中解析的内容。 - Brannon

4

虽然这不是你问题的确切答案,但如果你刚接触C#,可以考虑以下想法:如果你使用自定义文本文件读取一些配置参数,你可能需要查看.NET中的XML序列化主题。

XML序列化提供了一种简单的方式来编写和读取XML格式的文件。例如,如果你有一个像这样的配置类:

public class Configuration
{
   public int intVal { get; set; }
   public float floatVal { get; set; }
   public string stringVal { get; set; }
}

您可以使用XmlSerializer类简单地保存和加载它:

public void Save(Configuration config, string fileName)
{
   XmlSerializer xml = new XmlSerializer(typeof(Configuration));
   using (StreamWriter sw = new StreamWriter(fileName))
   {
       xml.Serialize(sw, config);
   }
}

public Configuration Load(string fileName)
{
   XmlSerializer xml = new XmlSerializer(typeof(Configuration));
   using (StreamReader sr = new StreamReader(fileName))
   {
       return (Configuration)xml.Deserialize(sr);
   }
}

Save方法如上所定义,将创建一个文件,其中包含以下内容:

<Configuration>
    <intVal>0</intVal>
    <floatVal>0.0</floatVal>
    <stringVal></stringVal>
</Configuration>

这种方法的好处是,如果您的Configuration类发生更改,您无需更改SaveLoad方法。


2
使用(StreamReader sr = new StreamReader(fileName)))这一行是错误的。你有三个)和两个(。 - VhsPiceros

4

我喜欢使用StreamReader来快速轻松地访问文件,就像这样...

  String file = "data_file.txt";    
  StreamReader dataStream = new StreamReader(file);   
  string datasample;
  while ((datasample = dataStream.ReadLine()) != null)
  {

     // datasample has the current line of text - write it to the console.
     Console.Writeline(datasample);
  }

5
注意,你没有在那里关闭文件 - 你应该有一个 using 语句(或者如果必须的话,使用 try/finally)。我还发现使用 File.OpenText 比调用 StreamReader 构造函数稍微简单一些。 - Jon Skeet
谢谢您的评论,我会查看File.OpenText。我现在陷入了StreamReader的困境。 - Paul

1

C#似乎没有像C++那样的格式化流读取器(如果我错了,我会很高兴被纠正)。因此,Jon Skeet的方法是将内容作为字符串读取并解析为所需类型。


这并没有回答问题。问题已经在五年前得到了回答,这应该让你停下来思考。 - John Saunders
你说得对。然而,OP问是否可以像C++一样完成。我回答了这个问题,虽然有点晚 :)。 - Ramashish Baranwal
我同意这些帖子都没有回答问题,因为它们都没有像C++版本那样做。我有完全相同的问题。流中没有换行符。而且文本流很大,所以在其上调用split是内存自杀。应该有一种逐个读取的方法。(到目前为止,我必须在我的代码中解析它) - Petr

0

0
这是我从文本文件中读取数字的代码。它演示了从文本文件“2 3 5 7 ...”中读取数字的概念。
public class NumberReader 
{
    StreamReader reader;

    public NumberReader(StreamReader reader)
    {
        this.reader = reader;
    }

    public UInt64 ReadUInt64()
    {
        UInt64 result = 0;

        while (!reader.EndOfStream)
        {
            int c = reader.Read();
            if (char.IsDigit((char) c))
            {
                result = 10 * result + (UInt64) (c - '0');
            }
            else
            {
                break;
            }
        }

        return result;
    }
}

以下是使用此类的示例代码:

using (StreamReader reader = File.OpenText("numbers.txt"))
{ 
    NumberReader numbers = new NumberReader(reader);

    while (! reader.EndOfStream)
    {
        ulong lastNumber = numbers.ReadUInt64();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接