Streamreader跳过行

穆罗

活跃成员
已加入
2018年4月30日
留言内容
26
编程经验
3-5
亲爱的朋友们,

我有这段代码,其中Streamreader有时会很好地读取文件,有时会跳过几行,我绝对不知道为什么要这样做。
C#:
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://myfile.txt");
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("user, "pw");
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(responseStream);

            while (!reader.EndOfStream)
            {
                MessageBox.Show(reader.ReadLine());
            }
            response.Close();
            reader.Close();
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
If "myfile.txt"是Unix文件格式,行尾标记与Windows上不同。
 

穆罗

活跃成员
已加入
2018年4月30日
留言内容
26
编程经验
3-5
我应该使用其他方法下载文件然后读取文件吗?因为实际上效果很好。这就是我得到的:
C#:
   using (WebClient webcl = new WebClient())
            {
                webcl.DownloadFileAsync(new Uri("http://file.txt/"), datei);
                webcl.DownloadFileCompleted += Webcl_DownloadFileCompleted;
            }
        }
        }

  private void Webcl_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
           if (e.Error == null)
            {
                foreach (string line in File.ReadLines(datei))
                {
                    ortstrassenTableAdapter.Insert(Ortid, line);
               }
               orteTableAdapter.UpdateStrassendatei(Strassendatei, Ortid);

            }
            else
            {
                MessageBox.Show("Ein Fehler ist aufgetreten!" + Environment.NewLine + e.Error.Message, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
   
        //}
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
比较通过FTP下载的文件和通过HTTP下载的文件的内容。 `fc.exe / b ftpdownload.txt httpdownload.txt`是否报告文件完全相同?我猜测它们是不同的,这是因为Web服务器代表您进行一些文件处理以设置一致的行尾。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
Aha! UseBinary defaults to true. If you set it to false as part of the FTP download, you should get the correct line endings on Windows:

FtpWebRequest.UseBinary
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
您的代码不喜欢我。

屏幕截图_9.jpg

如果不将文件从流中远程获取到磁盘,为什么要使用FTP?如果要使用流并读取到内存插槽,请使用Web客户端,然后在downloadComepleted事件确认文件已完成下载之后,就可以处理完整文件,就像上面所做的一样。然后将其读入内存,前提是该文件不大。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
If "myfile.txt"是Unix文件格式,行尾标记与Windows上不同。
UNIX上的perl文件夹中应该有一个小程序可以解决此问题。 @跳伞 Windows与Unix线尾 这是来自我们的恐怖编码伙伴。它涵盖换行符 这Great Newline Schism

虽然,似乎流已被丢弃,所以无法再在读取器中读取。如果OP在Try Catch中盲目使用此代码而不处理错误,它将看起来好像文件在中途中断。抱歉,我今天没有更多时间来玩这个游戏。我需要深入研究一些ASP工作项目。
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
是的,这也在我身上发生 Sheepings. -了解原因很重要。

这将带给您一些::的乐趣
C#:
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://127.0.0.1/CFile.txt");
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("admin", "admin");
            using (FtpWebResponse response = (FtpWebResponse)request.GetResponse())
            using (Stream responseStream = response.GetResponseStream())
            {
                var FileName = "SampleFile.txt";
                using (Stream fileStream = File.Create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), FileName)))
                {
                    responseStream.CopyTo(fileStream);
                }
            }
考虑一下。为什么在运行该异常时不抛出该异常?注意我所做的更改。

没有更多的错误。但是我同意,您最好使用webclient方法。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
如果你看下 源代码 for the WebClient it uses WebRequests anyway. The WebClient just gives a us a much easier interface to think and reason with.

Frankly, I'm confused at to why the underlying network stream would be disposed in the code from post #1. The responseresponseReader are not closed until after the while loop. The responseReader has a reference to the responseStream so the garbage collector shouldn't be trying to dispose of the stream, the response object, nor the reader while the loop is still running. I guess I'll just have to try to setup a repro case and try debugging.
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
XAMPP有一个FTP服务器,您可以安装Skydiver。您只需要在其后端添加用户和组,然后登录。
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
可以使用XAMPP代替IIS。用一个40行的7KB文本文件进行测试。

嗯...控制台应用程序也不例外...
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace TestFtp
{
    class Program
    {
        static void Main(string[] args)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://localhost/x64.txt");
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("user", "password");
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(responseStream);

            while (!reader.EndOfStream)
            {
                Console.WriteLine(reader.ReadLine());
            }
            response.Close();
            reader.Close();
        }
    }
}

接下来要尝试WinForms ...也不例外。
C#:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace TestFtpWinForms
{
    class TestForm : Form
    {
        public TestForm()
        {
            Text = "Test Form";
            var button = new Button() { Text = "Download", Location = new Point(20, 20) };
            button.Click += (o, e) => Download();
            Controls.Add(button);
        }

        void Download()
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://localhost/x64.txt");
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("user", "password");
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(responseStream);

            while (!reader.EndOfStream)
            {
                MessageBox.Show(reader.ReadLine());
            }
            response.Close();
            reader.Close();
        }
    }

    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new TestForm());
        }
    }
}

我尝试运行控制台和winforms应用程序,每次大约7次。没有引发异常。我需要更大的测试文件吗?我还尝试了具有相同结果的Linux行尾的测试文件。
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
啊哈!我得到它来重现问题。该文件不必以空行结尾。这不会引起异常:
C#:
Test 1
Test 2
Test 3
〜这行实际上是空白的〜
这"〜这行实际上是空白的〜"应该为空白行。在线论坛编辑器不会让我在此处留空。


但这会
C#:
Test 1
Test 2
Test 3

现在该做一些调试了...
 
Last edited:

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
根据Johns的链接,文件的结尾并不重要,当由流阅读器解析并被Microsoft文档排除时,文件的结尾似乎是正确的。

我已经附上了我使用过的文件。

我认为正在下载的文件将由完成下载的文件流处理,而读者正在阅读,因此会在流中引发异常。但是后来我想,微软一定会允许最终用户管理和处理自己的流。正确的?我突然意识到该文件可能以某种方式负责了该错误,但我现在没有,也没有足够的时间来进行查找。

有趣的是,它会在空白行上晃动摇摆器。但是还有其他空行,那么为什么不把空行放在其中之一上呢?为什么是最后一个?如果您有时间进行更深入的研究,我很想知道为什么。我仍然认为可能是其他原因造成的。您测试了几次?

顺便说一句,我的文件没有空的最后一行。 🤪
 

附件

  • CFile.txt
    256 bytes · Views: 40

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
Here's the reason for the exception thrown by StreamReader.EndOfStream:
StreamReader.ReadLine() 称自己 ReadBuffer() 作为建立返回文本行的工作的一部分。 ReadBuffer()调用流的Read()方法,如下所示是相关代码:
C#:
        internal virtual int ReadBuffer() {
            charLen = 0;
            charPos = 0;

            if (!_checkPreamble)
                byteLen = 0;
            do {
                if (_checkPreamble) {
                    Contract.Assert(bytePos <= _preamble.Length, "possible bug in _compressPreamble.  Are two threads using this StreamReader at the same time?");
                    int len = stream.Read(byteBuffer, bytePos, byteBuffer.Length - bytePos);
                    Contract.Assert(len >= 0, "Stream.Read returned a negative number!  This is a bug in your stream class.");
            :

在这种情况下,流的Read()实现是 FtpDataStream.Read():
C#:
        public override int Read(byte[] buffer, int offset, int size) {
            CheckError();
            int readBytes;
            try {
                readBytes = m_NetworkStream.Read(buffer, offset, size);
            } catch {
                CheckError();
                throw;
            }
            if (readBytes == 0)
            {
                m_IsFullyRead = true;
                Close();
            }
            return readBytes;
        }
注意它如何决定打电话 关闭() 读取零字节时自动释放。

更新后: 哎呀。我忘了提一下,ReadLine()本质上一直保持调用ReadBuffer()直到读取一行,否则无法读入更多字节。由于该行在文件末尾结束,因此对 do-while结束时的ReadBuffer() 导致FTP数据流类读取零字节。

StreamReader.EndOfStream 还调用ReadBuffer(),如果读取零字节,则返回true。不幸的是,如上所述,ReadBuffer()最终将调用流的Read()方法-kaboom!
 
Last edited:

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
Looks to be a bug in the FtpDataStream class. The regular MemoryStream doesn't suffer from the same problem. Here's the console code from post #15 with an extra step to copy from the FTP data stream over into a memory stream.
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace TestFtp
{
    class Program
    {
        static void Main(string[] args)
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://localhost/x64.txt");
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("user", "password");
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();

            Stream copyStream = new MemoryStream();
            responseStream.CopyTo(copyStream);
            copyStream.Seek(0, SeekOrigin.Begin);

            StreamReader reader = new StreamReader(copyStream);

            while (!reader.EndOfStream)
            {
                Console.WriteLine(reader.ReadLine());
            }
            response.Close();
            reader.Close();
        }
    }
}
 

羊皮

退休程序员
工作人员
已加入
2018年9月5日
留言内容
1,926
地点
英国
编程经验
10+
我有一天晚上会深入研究。有空的时候晚上。因此,毕竟,我的预感得到了证实,有些东西正在处理,而恰好是来自FTP的流。正确的?

我会将其添加为书签,以便在本周内重新阅读。做得好
 

跳伞

工作人员
已加入
2019年4月6日
留言内容
2,533
地点
弗吉尼亚州切萨皮克
编程经验
10+
这是一种重现问题的方法,而无需使用特殊文件,结尾没有空行:
C#:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace TestFtp
{
    class Program
    {
        static void Main()
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://localhost/x64.txt");
            request.Method = WebRequestMethods.Ftp.DownloadFile;
            request.Credentials = new NetworkCredential("user", "password");
            FtpWebResponse response = (FtpWebResponse)request.GetResponse();
            Stream responseStream = response.GetResponseStream();

            var buffer = new byte[1];

            // Read zero bytes
            responseStream.Read(buffer, 0, 0);

            // Now try to read 1 byte
            responseStream.Read(buffer, 0, 1);
        }
    }
}
 
最佳 底部