【问题标题】:Reading a text file word by word逐字读取文本文件
【发布时间】:2017-09-26 01:43:46
【问题描述】:

我有一个文本文件,其中只包含小写字母,除空格外没有标点符号。我想知道按字符读取文件的最佳方法,如果下一个字符是空格,则表示一个单词的结尾和新单词的开头。即在读取每个字符时将其添加到字符串中,如果下一个字符是空格,则将单词传递给另一个方法并重置,直到读取器到达文件末尾。

我正在尝试使用 StringReader 执行此操作,如下所示:

public String GetNextWord(StringReader reader)
{
    String word = "";
    char c;
    do
    {
        c = Convert.ToChar(reader.Read());
        word += c;
    } while (c != ' ');
    return word;
}

并将 GetNextWord 方法放在一个 while 循环中,直到文件末尾。这种方法有意义还是有更好的方法来实现这一点?

【问题讨论】:

  • 请不要在标题前加上“C#:”之类的前缀。这就是标签的用途。
  • 我认为你应该从文件中读取更大的块(比如 4096 字节),否则看起来很好......另外,我想知道它的最佳大小是什么 :)
  • 这样构建字符串会生成很多对象(记住字符串是不可变的)。如果您想在读取文件时构建字符串,请使用StringBUilder
  • @ Niko 我该怎么做,因为我一次仍然需要 1 个字符? @Brian 好的,我会检查一下,谢谢您的帮助
  • 如果您想要最快的方式并且有足够的内存,请使用新的MemoryMappedFile 类。

标签: c#


【解决方案1】:

有一个更好的方法:string.Split():如果你读入整个字符串,C# 可以自动将其拆分为每个空格:

string[] words = reader.ReadToEnd().Split(' ');

words 数组现在包含文件中的所有单词,您可以对它们进行任何操作。

此外,您可能需要研究 System.IO 命名空间中的 File.ReadAllText 方法 - 它可以让您将文件导入文本变得更加轻松。

编辑:我猜这是假设您的文件不是非常大;只要整个事情可以合理地读入内存,这将最容易工作。如果您有千兆字节的数据要读入,您可能会想要回避这一点。不过,如果可能的话,我建议使用这种方法:它可以更好地利用您可以使用的框架。

【讨论】:

  • 这最终将用于非常大的文本文件,所以这仍然有效吗?
  • 方便,当然。 “最大限度地利用框架”,嗯,真的,没有。
  • 书本大小,我会用孙子兵法之类的开源书籍
  • 据此 (gutenberg.org/ebooks/132) 它只有几百千字节 - 可能不会成为问题。我刚刚使用File.ReadAllText 导入了它——它会立即在' ' 上读取并拆分为大约61k 字。
  • 我自己检查了一下,我不确定文件大小是否很重要,或者字数是否太多而无法处理。感谢您的帮助:)
【解决方案2】:

如果您对即使在非常大的文件上也有良好的性能感兴趣,您应该看看新的(4.0)MemoryMappedFile-Class

例如:

using (var mappedFile1 = MemoryMappedFile.CreateFromFile(filePath))
{
    using (Stream mmStream = mappedFile1.CreateViewStream())
    {
        using (StreamReader sr = new StreamReader(mmStream, ASCIIEncoding.ASCII))
        {
            while (!sr.EndOfStream)
            {
                var line = sr.ReadLine();
                var lineWords = line.Split(' ');
            }
        }  
    }
}

来自 MSDN:

内存映射文件将文件的内容映射到应用程序的 逻辑地址空间。内存映射文件使程序员能够工作 非常大的文件,因为内存可以同时管理, 它们允许对文件进行完全、随机的访问,而无需 寻求。内存映射文件也可以跨多个共享 进程。

CreateFromFile 方法从一个 磁盘上现有文件的指定路径或 FileStream。变化 文件未映射时会自动传播到磁盘。

CreateNew 方法创建一个未映射的内存映射文件 到磁盘上的现有文件;并且适合创建共享 用于进程间通信 (IPC) 的内存。

内存映射文件与名称相关联。

您可以创建内存映射文件的多个视图,包括 文件部分的视图。您可以将文件的相同部分映射到 多个地址创建并发内存。对于两个视图 保持并发,它们必须从相同的内存映射创建 文件。使用两个视图创建同一文件的两个文件映射 不提供并发。

【讨论】:

    【解决方案3】:

    首先:StringReader 从一个已经在内存中的字符串读取。这意味着您必须先加载整个输入文件,然后才能从中读取,这违背了一次读取几个字符的目的;如果输入非常大,它也可能是不可取的,甚至是不可能的。

    从文本(它是对数据源的抽象)读取的类是StreamReader,您可能想改用那个类。现在StreamReaderStringReader 共享一个抽象基类TextReader,这意味着如果您针对TextReader 进行编码,那么您可以两全其美。

    TextReader 的公共接口确实支持您的示例代码,所以我认为这是一个合理的起点。您只需要修复一个明显的错误:没有检查 Read 返回 -1(表示可用数据结束)。

    【讨论】:

    • 谢谢你,一个非常有用的答案。我会调查这些事情,但由于我提出问题的方式,我必须将 eouw0o83hf 的答案标记为已接受,我认为这对其他有类似问题的人会更有帮助。再次感谢:)
    【解决方案4】:

    全部在一行,给你(假设是 ASCII,也许不是 2gb 文件):

    var file = File.ReadAllText(@"C:\myfile.txt", Encoding.ASCII).Split(new[] { ' ' });
    

    这将返回一个字符串数组,您可以对其进行迭代并执行任何您需要的操作。

    【讨论】:

    • .Split(' ') - 无需创建数组
    • @eouw0o83hf:它编译为相同的 IL,所以没关系。
    【解决方案5】:

    我会这样做:

    IEnumerable<string> ReadWords(StreamReader reader)
    {
        string line;
        while((line = reader.ReadLine())!=null)
        {
            foreach(string word in line.Split(new [1] {' '}, StringSplitOptions.RemoveEmptyEntries))
            {
                yield return word;
            }
        }
    }
    

    如果使用 reader.ReadAllText,它会将整个文件加载到您的内存中,因此您可能会遇到 OutOfMemoryException 和许多其他问题。

    【讨论】:

      【解决方案6】:

      如果你想在不分割字符串的情况下读取它 - 例如行太长,所以你可能会遇到 OutOfMemoryException,你应该这样做(使用 streamreader):

      while (sr.Peek() >= 0)
      {
          c = (char)sr.Read();
          if (c.Equals(' ') || c.Equals('\t') || c.Equals('\n') || c.Equals('\r'))
          {
              break;
          }
          else
              word += c;
      }
      return word;
      

      【讨论】:

        【解决方案7】:

        这种方法会拆分您的单词,而它们之间用空格或超过 1 个空格(例如两个空格)分隔/

        StreamReader streamReader = new StreamReader(filePath); //get the file
        string stringWithMultipleSpaces= streamReader.ReadToEnd(); //load file to string
        streamReader.Close();
        
        Regex r = new Regex(" +"); //specify delimiter (spaces)
        string [] words = r.Split(stringWithMultipleSpaces); //(convert string to array of words)
        
        foreach (String W in words)
        {
           MessageBox.Show(W);
        }
        

        【讨论】:

          【解决方案8】:

          我使用您提到的文件根据您的确切要求创建了一个简单的控制台程序,它应该易于运行和检查。请找到随附的代码。希望这会有所帮助

          static void Main(string[] args)
              {
          
                  string[] input = File.ReadAllLines(@"C:\Users\achikhale\Desktop\file.txt");
                  string[] array1File = File.ReadAllLines(@"C:\Users\achikhale\Desktop\array1.txt");
                  string[] array2File = File.ReadAllLines(@"C:\Users\achikhale\Desktop\array2.txt");
          
                  List<string> finalResultarray1File = new List<string>();
                  List<string> finalResultarray2File = new List<string>();
          
                  foreach (string inputstring in input)
                  {
                      string[] wordTemps = inputstring.Split(' ');//  .Split(' ');
          
                      foreach (string array1Filestring in array1File)
                      {
                          string[] word1Temps = array1Filestring.Split(' ');
          
                          var result = word1Temps.Where(y => !string.IsNullOrEmpty(y) && wordTemps.Contains(y)).ToList();
          
                          if (result.Count > 0)
                          {
                              finalResultarray1File.AddRange(result);
                          }
          
                      }
          
                  }
          
                  foreach (string inputstring in input)
                  {
                      string[] wordTemps = inputstring.Split(' ');//  .Split(' ');
          
                      foreach (string array2Filestring in array2File)
                      {
                          string[] word1Temps = array2Filestring.Split(' ');
          
                          var result = word1Temps.Where(y => !string.IsNullOrEmpty(y) && wordTemps.Contains(y)).ToList();
          
                          if (result.Count > 0)
                          {
                              finalResultarray2File.AddRange(result);
                          }
          
                      }
          
                  }
          
                  if (finalResultarray1File.Count > 0)
                  {
                      Console.WriteLine("file array1.txt contians words: {0}", string.Join(";", finalResultarray1File));
                  }
          
                  if (finalResultarray2File.Count > 0)
                  {
                      Console.WriteLine("file array2.txt contians words: {0}", string.Join(";", finalResultarray2File));
                  }
          
                  Console.ReadLine();
          
              }
          }
          

          【讨论】:

            【解决方案9】:

            此代码将根据 Regex 模式从文本文件中提取单词。您可以尝试使用其他模式,看看哪种模式最适合您。

                StreamReader reader =  new StreamReader(fileName);
            
                var pattern = new Regex(
                          @"( [^\W_\d]              # starting with a letter
                                                    # followed by a run of either...
                              ( [^\W_\d] |          #   more letters or
                                [-'\d](?=[^\W_\d])  #   ', -, or digit followed by a letter
                              )*
                              [^\W_\d]              # and finishing with a letter
                            )",
                          RegexOptions.IgnorePatternWhitespace);
            
                string input = reader.ReadToEnd();
            
                foreach (Match m in pattern.Matches(input))
                    Console.WriteLine("{0}", m.Groups[1].Value);
            
                reader.Close();       
            

            【讨论】:

              猜你喜欢
              • 2022-01-16
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-04-12
              • 2015-05-13
              相关资源
              最近更新 更多