简介C# FTP下载实例源码是一份面向.NET开发初学者的网络操作练习资源围绕FTP协议的文件下载场景演示如何使用FtpWebRequest、FtpWebResponse等系统类完成服务器连接、登录、读取数据流与本地落盘等核心步骤。压缩包内含21个文件共65KB以cs源码、sln工程、resx资源及exe可执行程序为主另有少量pdb调试符号与resources文件整体体量轻巧适合直接打开工程对照学习。当前已有692人浏览学习。除基础下载流程外源码还涉及SSL/TLS安全连接、被动模式设置、流复制与资源释放等关键写法可作为断点续传、并行下载、进度显示等进阶改造的起点。通过研读这份完整工程读者既能巩固C#网络编程基础也能快速将FTP下载功能迁移到实际项目中。1. 一个能跑通的 C# FTP 下载实例网络操作不是只有 Socket 一种写法做上位机或者桌面工具的人早晚会遇到一个需求从服务器拉一份配置、拉一个固件、同步一批数据文件。这时候你以为要上 Socket 自己拼协议其实 C# 里System.Net.FtpWebRequest已经把 FTP 下载这件事封得很薄了薄到你可以边看代码边学会整个链路。这份 C# FTP 下载实例源码就是一个完整的 VS 解决方案打开DFTPFile.sln就能跑界面在Form1.cs里下载逻辑写在按钮事件中用的是最标准的FtpWebRequest加响应流读写的套路。适合两类人刚接触 C# 网络编程、想搞懂 FTP 到底怎么连怎么下的新手以及急着交付工具、想找一份能改能用的参考代码的开发者。它解决的问题很具体连接 FTP 服务器、登录、列目录、下载文件、显示进度。2. 从 FtpWebRequest 到响应流下载链路的每个环节都能单独调2.1 一个请求对象说清楚 FTP 下载的全部配置FTP 下载的核心入口是FtpWebRequest它不直接帮你传文件而是把一个下载动作拆成「连接、登录、发指令、收数据」几步你只需要配置它然后从响应里拿流。先看这份源码里最基础的一段FtpWebRequest request (FtpWebRequest)WebRequest.Create(ftp://192.168.1.100/pub/app.zip); request.Method WebRequestMethods.Ftp.DownloadFile; // 明确这是下载动作 request.Credentials new NetworkCredential(ftpuser, ftp123); request.UseBinary true; // 二进制模式别用文本模式 request.UsePassive true; // 被动模式多数场景下必须开 request.KeepAlive false; // 下载完就断开避免连接堆积 request.Timeout 10000; // 连接超时 10 秒单位毫秒 request.ReadWriteTimeout 30000; // 读数据超时 30 秒这段代码把 FTP 下载的关键参数全部压在一个对象上。Method决定了这次请求要做什么DownloadFile是下载ListDirectory和ListDirectoryDetails是列目录后面还会用到。UseBinary很多人会漏掉默认值是true但如果你显式设成false服务器会按文本模式传输换行符会被转换下下来的二进制文件直接损坏这是一类非常隐蔽的翻车点。UsePassive是另一个高危参数后面避坑章单独讲。这里还有一个新手容易犯的错Credentials不能传null即便服务器允许匿名登录也要给一个空的NetworkCredential(anonymous, anonymous)。不设凭据多数 FTP 服务器会直接回 530。另外Timeout只控制建立连接和发送命令的等待时间数据流的读取超时由ReadWriteTimeout控制两个都要设只设一个会在慢速网络下卡死。2.2 响应流与本地文件的对接缓冲区怎么选请求配置好之后下载动作本身是拿到响应流然后循环读、循环写。这份源码里的核心下载循环是这样的FtpWebResponse response (FtpWebResponse)request.GetResponse(); using (Stream responseStream response.GetResponseStream()) using (FileStream localFileStream new FileStream(D:\download\app.zip, FileMode.Create)) { byte[] buffer new byte[81920]; // 80KB 缓冲区 int bytesRead; long totalBytes 0; while ((bytesRead responseStream.Read(buffer, 0, buffer.Length)) ! 0) { localFileStream.Write(buffer, 0, bytesRead); totalBytes bytesRead; // 这里回调节进度totalBytes / response.ContentLength } } response.Close();逻辑不复杂GetResponse触发与服务器的数据连接GetResponseStream拿到数据流本地FileStream负责落盘。缓冲区大小我见过有人用 1024 字节那是按教程抄的实际跑起来慢得难受81920是FileStream内部缓冲的默认上限用它做读写缓冲区效率最高如果你拷的是几十 MB 的大文件能明显感觉到差距。注意response.ContentLength可以用来算进度但不要迷信它。有些 FTP 服务器在DownloadFile模式下不返回准确的ContentLength可能为 0 或者 -1这时候进度条会先跑到 100% 再跳一下属于正常现象。稳妥的做法是以本地累计读到的字节数为准ContentLength只做参考。还有一点response.Close()必须执行否则数据连接不释放连续下载多个文件时会出现连接数耗尽表现为下载到第三个文件突然变慢或者直接卡住。2.3 常见做法把下载逻辑从界面里拆出来这份源码的下载代码写在Form1.cs的按钮事件里演示可以但实际项目里我强烈建议拆成一个独立方法因为下载逻辑一旦混在 UI 事件里后面加断点续传、加重试、加并发时都无从下手。我一般会先封装一个同步方法public bool DownloadFile(string serverFilePath, string localFilePath, string userName, string password, out string error) { error string.Empty; try { FtpWebRequest request (FtpWebRequest)WebRequest.Create(serverFilePath); request.Method WebRequestMethods.Ftp.DownloadFile; request.Credentials new NetworkCredential(userName, password); request.UseBinary true; request.UsePassive true; request.KeepAlive false; using (FtpWebResponse response (FtpWebResponse)request.GetResponse()) using (Stream stream response.GetResponseStream()) using (FileStream fs new FileStream(localFilePath, FileMode.Create)) { byte[] buffer new byte[81920]; int read; while ((read stream.Read(buffer, 0, buffer.Length)) 0) { fs.Write(buffer, 0, read); } } return true; } catch (Exception ex) { error ex.Message; return false; } }封装的好处有两个。第一调用方不用关心FtpWebRequest的细节传入服务器路径、本地路径、账号密码就行返回值直接告诉上层成功还是失败。第二这个方法的边界很干净后续要加ContentOffset断点续传只需要在这个方法内部加一个偏移参数调用方完全无感。文件路径拼接有个小坑服务器路径必须以ftp://开头并且目录分隔符用/而不是\很多初学者把 Windows 路径习惯带进来导致服务器一直报 550。3. 文件浏览与目录下载ListDirectoryDetails 的解析是第一个硬骨头3.1 用 ListDirectoryDetails 拿到服务器文件列表如果只是下载固定路径的文件第二章的代码已经够了。但实际产品里用户总想在界面上看到服务器上有哪些文件这时候就需要先列目录。FTP 的列目录有两种方法ListDirectory和ListDirectoryDetails区别在于前者只返回文件名后者返回带权限、大小、日期、文件名的完整行。做界面浏览时建议用ListDirectoryDetails因为文件大小和修改时间通常要展示出来而且后面按文件大小判断是否断点续传也用得到FtpWebRequest request (FtpWebRequest)WebRequest.Create(ftp://192.168.1.100/pub/); request.Method WebRequestMethods.Ftp.ListDirectoryDetails; request.Credentials new NetworkCredential(ftpuser, ftp123); request.UsePassive true; FtpWebResponse response (FtpWebResponse)request.GetResponse(); StreamReader reader new StreamReader(response.GetResponseStream()); string line; while ((line reader.ReadLine()) ! null) { listBox1.Items.Add(line); // 先把原始行显示出来调试阶段别跳步 } reader.Close(); response.Close();这里有个习惯问题我建议调试时先把每一行原始文本直接丢到 ListBox 里看一遍而不是急着写解析代码。因为不同 FTP 服务器返回的目录行格式差异巨大你以为是文件名在最后、大小在第 5 列换一台服务器可能全变样。先看原始输出再写解析能省下大量瞎猜的时间。3.2 UNIX 与 Windows 目录行格式的不一致ListDirectoryDetails返回的格式没有国际标准RFC 959 只说返回系统相关的信息。最常见的是 UNIX 风格长这样-rw-r--r-- 1 ftp ftp 123456 Jan 01 10:30 app.zip drwxr-xr-x 2 ftp ftp 4096 Jan 01 10:30 config但如果你连的是 Windows 自带的 IIS FTP 或者某些国产 FTP 服务器返回的可能是01-01-23 10:30AM 123456 app.zip写解析逻辑时要兼容这两种风格我常用的做法是先判断行首是不是d确定目录还是文件再用空格切分从尾部倒着取文件名——因为文件名本身可能带空格从头部正着切一定会翻车public static FileEntry ParseFtpLine(string line) { FileEntry entry new FileEntry(); if (string.IsNullOrWhiteSpace(line)) return null; if (line.StartsWith(d)) { entry.IsDirectory true; string[] parts line.Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries); entry.Name parts[parts.Length - 1]; // UNIX 风格文件名在最后 return entry; } if (line.StartsWith(-)) { string[] parts line.Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries); long size; long.TryParse(parts[parts.Length - 5], out size); // 大小是倒数第 5 个字段 entry.Size size; entry.Name parts[parts.Length - 1]; return entry; } // 兼容 IIS 风格日期在前大小在倒数第 2 个字段 string[] iisParts line.Split(new char[] { }, StringSplitOptions.RemoveEmptyEntries); entry.Name string.Join( , iisParts, 2, iisParts.Length - 2); return entry; }这段代码的要点是「从尾部解析」。UNIX 风格里大小在倒数第 5 个字段但如果文件名里有空格Split之后从头部数就全乱了从尾部算文件名取最后一段或最后几段拼接反而稳定。IIS 风格的日期格式带AM/PM切分后第 0、1 是日期时间第 2 是大小第 3 到末尾是文件名所以用string.Join把剩余部分拼回来。这段解析代码不完美但覆盖了最常见的两种服务器对你手头的项目来说已经够用。3.3 我的做法逐文件下载与进度回调拿到文件列表后用户勾选一批文件点下载界面上要能实时看到进度。WinForms 里做进度回调用BackgroundWorker或者IProgressT都行不要直接在 UI 线程里跑下载循环界面会假死。我一般会把下载方法改成带IProgressint参数的版本配合ProgressT更新进度条public void DownloadFiles(Liststring serverFiles, string localDir, string user, string pwd, IProgressint progress) { int completed 0; foreach (string serverFile in serverFiles) { string fileName Path.GetFileName(serverFile); string localPath Path.Combine(localDir, fileName); if (DownloadFile(serverFile, localPath, user, pwd, out string error)) { completed; progress?.Report(completed * 100 / serverFiles.Count); } else { // 记录 error 到日志别中断整批下载 } } }调用时用Progressint实例回调更新进度条因为ProgressT会自动把回调封送到创建它的 SynchronizationContext 上也就是 UI 线程不需要你手动Invoke。这里有一个关键决策单个文件下载失败是中断整批还是跳过继续我一般选择跳过并记录日志因为 FTP 下载经常是批量任务一个文件失败中断全部代价太高。但如果这批文件之间有依赖关系那就另说。4. 避坑FTP 下载最容易翻车的五个地方每个都附排查路径4.1 连接与模式层面500 Illegal PORT 与 425 Use PASV first这是搜索热词里最典型的一条ftp ls 500 illegal port command. 425 use port or pasv first.。现象是代码或命令行里连接正常一执行ls或下载就报 500/425。原因是主动模式Active Mode下客户端在数据连接时暴露了自己的 IP 和端口如果客户端在 NAT 后面或者服务器有防火墙策略数据连接根本建立不起来。解决request.UsePassive true; // 被动模式客户端主动发起数据连接但反过来的情况也存在某些内网部署的老式 FTP 服务器只支持主动模式设了UsePassive true反而连不上。所以这个坑的实际解法是默认用被动模式连不上时尝试把UsePassive改成false再试一次两种模式都失败再报错给用户。顺便说一下主动和被动模式的区别在于数据连接的发起方主动模式是服务器连客户端被动模式是客户端连服务器的随机高位端口。公司网络环境复杂时被动模式通常更可靠因为防火墙只拦入站不拦出站。4.2 传输与文件层面连接被强制关闭与本地文件被占用现象是下载大文件时抛WebException: 无法将数据写入传输连接: 远程主机强迫关闭了一个现有的连接或者读流读到一半Read返回 0。这类报错的常见原因有三个一是ReadWriteTimeout设置过短数据连接空闲超时二是下载过程中网络抖动TCP 连接被重置三是服务器主动断开了数据连接比如超出并发限制。解决思路是先确认现象出现的时机——如果总是在下载到同一进度附近断优先怀疑服务器限制如果是随机时间段断优先怀疑网络超时request.ReadWriteTimeout 60000; // 读超时拉长到 60 秒然后给下载套重试逻辑注意重试要针对连接中断这类瞬时错误不要对文件不存在做无意义重试。另一个文件层面的坑本地文件打不开报IOException: 文件正在被另一个进程使用。原因多半是防病毒软件正在扫描你刚生成的临时文件或者上一个进程没有释放FileStream。排查时先关掉杀毒软件的实时防护验证是不是它的锅代码侧的处理是写临时文件再改名或者全部用using确保流被释放。4.3 协议与环境层面SSL 证书和被动模式端口范围EnableSsl true之后连接直接抛证书链错误的坑也经常遇到。原因很简单很多内网 FTP 服务器用的是自签名证书FtpWebRequest默认会校验证书链。解决方法是写一个证书验证回调仅在内网可信环境使用ServicePointManager.ServerCertificateValidationCallback (sender, certificate, chain, sslPolicyErrors) true;这段代码会让当前进程内所有 HTTPS/FTP SSL 请求跳过证书校验安全性上属于自己承担风险的写法只建议在调试阶段或内网可控环境使用不建议直接搬进生产环境生产环境应该把服务器证书导入信任根。另一个环境层面的隐藏坑被动模式下服务器会开放一段高位端口范围如果服务器防火墙只开放了 21 端口数据连接会一直失败。这种问题在客户端怎么改都没用只能让服务器管理员开放数据端口范围或者改用主动模式。4.4 UseBinary 遗忘下载的文件跟服务器上的不一样如果你用FtpWebRequest下载一个 zip 或 exe本地文件能打开但校验不通过或者解压报错十有八九是UseBinary没有显式设置。虽然FtpWebRequest默认UseBinary true但有些代码在复制粘贴过程中把它丢了或者请求对象被反复使用。文本模式下服务器会把换行符从\n转成\r\n二进制文件被改了几个字节表面上文件大小对、打开不报错但哈希不一致。排查可以用 Notepad 十六进制模式打开本地文件对比服务器原文件头几个字节如果发现0D 0A和服务器的不一致基本就是文本模式传输了。解决就是一行request.UseBinary true;。4.5 断点续传被服务器拒绝ContentOffset 不是万能的后面第 5 章会详细写断点续传这里先说坑request.ContentOffset设置后服务器需要支持 REST 命令如果服务器不支持或者文件本身不允许续传请求会直接回报错。现象就是下载到的文件开头缺了一段或者服务器返回 550 表示命令不支持。排查方法是先用 FTP 命令行连上去敲rest 100看服务器是否回 350不回就趁早放弃续传逻辑老老实实全量下载。5. 断点续传与并行下载从单文件跑通到多文件并发5.1 ContentOffset 实现断点续传一个属性解决断点续传在FtpWebRequest里实现成本极低核心就是ContentOffset。它的语义是从服务器文件的第 N 个字节开始传本地打开文件时用追加模式接着写。注意断点续传的前提是本地已有一个部分文件并且这个文件的内容和服务器文件前 N 字节完全一致否则续传等于白传public bool DownloadFileResume(string serverFilePath, string localFilePath, string user, string pwd) { long existingSize 0; if (File.Exists(localFilePath)) { existingSize new FileInfo(localFilePath).Length; // 已有文件大小 } FtpWebRequest request (FtpWebRequest)WebRequest.Create(serverFilePath); request.Method WebRequestMethods.Ftp.DownloadFile; request.Credentials new NetworkCredential(user, pwd); request.UseBinary true; request.UsePassive true; request.ContentOffset existingSize; // 从断点位置继续 using (FtpWebResponse response (FtpWebResponse)request.GetResponse()) using (Stream stream response.GetResponseStream()) using (FileStream fs new FileStream(localFilePath, existingSize 0 ? FileMode.Append : FileMode.Create)) { byte[] buffer new byte[81920]; int read; while ((read stream.Read(buffer, 0, buffer.Length)) 0) { fs.Write(buffer, 0, read); } } return true; }这里有个边界条件要处理如果服务器不支持断点续传GetResponse()阶段就会抛异常这时可以捕获异常后回退到全量下载也就是把ContentOffset重置为 0。兼容写法是 catch 住WebException判断response.Status是CommandNotImplemented或ActionNotTakenFileUnavailable然后走全量下载分支。还有一个更稳的方案下完之后拿服务器文件大小和本地文件大小做一次对比不一致就删除本地文件重新下载。这种做法叫校验后重下比盲目续传可靠得多代价是多一次GetFileSize请求。5.2 并行下载的限度FTP 服务器的连接数限制多文件下载提升效率最直接的办法是并发但 FTP 服务器的并发限制是一个硬边界。很多 Windows 自带 IIS FTP 默认限制同一 IP 的连接数超过后直接拒绝新连接或者让已有连接超时。所以并行下载不能无脑开线程我会用一个SemaphoreSlim把并发数限制在 3private static SemaphoreSlim _semaphore new SemaphoreSlim(3); public async Task DownloadWithLimitAsync(string serverFile, string localFile, string user, string pwd) { await _semaphore.WaitAsync(); try { await Task.Run(() DownloadFile(serverFile, localFile, user, pwd, out _)); } finally { _semaphore.Release(); } }并发数设多少合适我的经验是如果服务器在你自己的内网3 到 5 比较稳妥如果是公网服务器2 到 3 就够太大容易被服务器限流甚至封 IP。调优时看服务器的日志或响应码如果频繁出现 421 Too many connections说明并发数已经超了降下来或者排队。5.3 超时与重试把下载器调到生产环境生产环境里的 FTP 下载重试逻辑是必需品但重试策略要分场景。我按WebExceptionStatus区分是否值得重试状态 / 响应码含义是否重试550文件不存在不重试直接记录错误530登录失败不重试检查账号密码425 / 426数据连接建立失败重试间隔加大连接被重置网络抖动重试退避重试超时服务器无响应重试一次再失败转人工重试逻辑的写法是有限次数 递增间隔不要无限重试。我第一次做 FTP 下载时犯过的错就是失败后立刻重试结果服务器还没恢复连续打过去全是失败反而加重了服务器负担。后来改成第一次失败等 3 秒第二次等 10 秒第三次等 30 秒最多重试 3 次明显稳多了。实现上可以用一个简单的循环int maxRetry 3; int delayMs 3000; for (int attempt 1; attempt maxRetry; attempt) { if (DownloadFile(serverFile, localFile, user, pwd, out string error)) return true; if (attempt maxRetry) Thread.Sleep(delayMs * attempt); // 3 秒、6 秒、9 秒递增 } return false;注意重试时要重新创建FtpWebRequest对象不要复用上一个请求对象。FtpWebRequest的一次性设计决定了它不能被重置后二次使用复用会抛异常或者行为诡异这是我踩过的一个很深很没必要的坑。6. 验证下载可靠性的几个土办法哈希比对与日志留痕源码能跑通只是第一步交付前我习惯做一轮土办法验证不依赖任何第三方测试框架。最有用的是下载完成后做哈希比对。服务器那边如果有文件的 MD5下完直接对比没有的话可以先全量下载一次作为基准之后每次改代码都重新下载并比哈希只要哈希一致就说明传输逻辑没被改坏。用 C# 算哈希很简单public static string ComputeMd5(string filePath) { using (var md5 System.Security.Cryptography.MD5.Create()) using (var stream File.OpenRead(filePath)) { byte[] hashBytes md5.ComputeHash(stream); return BitConverter.ToString(hashBytes).Replace(-, ).ToLower(); } }我一般会用 1 个几十 MB 的文件和 1 个几百 MB 的文件做测试分别验证小文件和大文件的完整性因为大文件更容易暴露缓冲区设置、超时处理和数据连接稳定性问题。第二个土办法是给下载方法加一个日志参数记录每一次下载的服务器地址、耗时、响应码、重试次数。不要小看这个日志它在上线后排查问题时是救命稻草。我遇到过的情况是运维说文件有时候下不下来没有日志的话根本没法定位是网络问题还是服务器问题加了两行日志后发现是服务器每天凌晨 3 点做备份导致连接被拒纯属服务器侧问题。第三个方法是模拟弱网环境。用带宽限制工具把网速限制到 200KB/s 左右下载一个 50MB 的文件看超时处理和断点续传是否正常。弱网是唯一能逼出超时和读流异常的途径很多代码在局域网里怎么跑都正常一到弱网环境就现出原形。我从那次被哈希不一致的教训教育过之后现在每次改完下载模块都会强制自己走一遍下载大文件 → 比对哈希 → 看日志这三个步骤哪怕只是改了一个超时参数。这种笨办法看起来慢实际是性价比最高的回归测试。希望这四个多小时的整理能帮到你尤其是那些准备把这份源码改造成自己工具的人先把前两章跑通再把避坑章的检查点过一遍你就能少走很多弯路。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
昇腾800I A2部署Kimi K2保姆级实战指南 1. 项目概述:为什么要在昇腾800I A2上部署Kimi K2?Kimi K2不是官方发布的模型名称,而是社区对Kimi系列中某一代推理优化版本的非正式代号——它特指适配国产算力平台、经量化剪枝与算子融合后,在昇腾架构上实测吞吐达120 tokens/s… · 2026/9/24 21:01:11
开源AI生成PPT工具PPTist实测:从大纲到成品全流程解析 我做了十年职场汇报,最讨厌的事就是做PPT。不是不会做,而是太耗时——结构要想、文案要磨、排版要调、配色要改,一套20页的片子折腾一个晚上是常态。后来我开始用AI辅助,试过让ChatGPT写大纲、让Midjourney出配图、再用Gamma这类在… · 2026/9/24 21:01:11
RDMA门铃机制与传输调优:从CPU/GPU控制路径到两跳聚合 RDMA跑得好的集群千篇一律,跑不好的集群各有各的“门铃”问题。这里说的门铃不是物理门铃,而是RDMA网卡上的Doorbell机制——你往发送队列里扔了一堆WQE,数据已经放在内存里了,但如果没人去按一下网卡的门铃寄存器,网卡… · 2026/9/24 21:01:04
Brepocitinib的结构特征、激酶选择性与质控研究要点 导语
双靶点激酶小分子是近年酶学与结构生物学研究里很活跃的一个方向。Brepocitinib(研发代号 PF-06700841,CAS: 1883299-62-4)是其中代表性化合物之一:它以 ATP 竞争方式作用于 TYK2 与 JAK1 两个激酶的催化域,同时与… · 2026/9/24 21:35:14
虚拟电厂广域聚合为何必须用Zonotope建模 简介:本资源是一份面向电力系统研究人员与Python开发者的技术实践资料,聚焦虚拟电厂(VPP)中空调负荷、储能设备和柴油发电机三类分布式资源的广域聚合与鲁棒调控问题,采用前沿的Zonotope(奇诺多面体&#x… · 2026/9/24 21:35:01
C语言实现围棋终局判定:从二维数组到死活判断 很多学C语言的朋友,学到数组、指针、结构体之后都会产生一种“我到底能用它做点什么”的疑问。写控制台计算器太简单,做图形界面又太复杂,“判断一个已下完的棋局的胜负”正好处在中间——它不要求你懂什么图形库,也不需要多高深的… · 2026/9/24 21:34:48
Word更新目录全攻略:从域原理到样式设置一次讲透 做标书、写论文、出报告的时候,目录这个东西绝对能把人逼疯。你辛辛苦苦把正文改完,想在打印前瞄一眼目录,结果发现页码还停在半个月前。更离谱的是,有时候你把目录更新一下,整个排版全乱了,三四级标题挤成… · 2026/9/24 21:34:48
将安全审计封装成Skill:面向AI编码代理的可复用工作流 1. 为什么安全审计要“做成一个 skill”先说结论:这个security-audit-skill,本质上不是传统意义上的安全扫描脚本,也不是一个单纯挂在聊天窗口里的“帮我审一下这段代码”的提示词,而是给AI编码代理(类似Codex、Claude… · 2026/9/24 21:34:48
JavaScript正则表达式与作用域:核心机制与实战指南 1. 项目概述与核心思路1.1 这个项目到底在解决什么问题先说说我为什么要把“正则表达式”和“作用域”这两个主题放在一起聊。很多初学JavaScript的朋友都会经历这样一个阶段:正则表达式好像在哪儿都能见到,但自己一写就抓瞎;作用域这个词听了… · 2026/9/24 21:34:48
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44