news 2026/5/7 12:06:08

Android端ChatGPT应用开发:WebSocket中转架构与流式对话实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Android端ChatGPT应用开发:WebSocket中转架构与流式对话实现

1. 项目概述与核心思路

最近在做一个Android端的ChatGPT对话应用,核心目标很简单:在手机上就能方便地和AI聊天。市面上虽然有不少现成的App,但要么需要付费,要么功能受限,要么就是数据隐私让人不放心。所以,我决定自己动手,基于OpenAI的官方API,从零开始构建一个完全可控的客户端。

这个项目ChatGPT_Android本质上是一个Android应用,它通过调用OpenAI的Chat Completions API,实现了与GPT系列模型的文本对话。但和直接调用API的简单Demo不同,我重点解决了几个在实际使用中必然会遇到的痛点:首先是网络问题,由于众所周知的原因,国内无法直接访问api.openai.com;其次是用户体验,比如流式响应、对话记忆、以及如何低成本地使用。整个项目的架构设计,就是围绕如何优雅地解决这些问题展开的。

我采用了客户端(Android App)与服务端(Spring Boot中转)分离的架构。App本身不直接请求OpenAI,而是通过一个我们自己部署的中转服务器进行通信,数据通过WebSocket(WSS)进行加密传输。这样做有几个好处:一是绕开了客户端的网络限制,用户无需在手机上配置复杂的网络环境;二是将敏感的API Key保存在服务端,避免了客户端泄露的风险;三是为后续功能扩展,比如模型路由、缓存、限流等,提供了可能。整个思路就是:把复杂的、受限制的部分放到服务端去处理,给客户端一个干净、稳定的接口。

2. 核心架构设计与技术选型解析

2.1 为什么选择客户端-服务端中转模式?

直接让Android App去调用https://api.openai.com是最简单的方案,但现实很骨感。首先就是网络连通性问题,这会导致App在大部分网络环境下直接不可用。其次,将API Key硬编码或存储在App内是极不安全的,很容易被反编译提取,导致密钥滥用和资金损失。最后,直接调用缺乏缓冲和控制,无法实现一些高级功能,比如对话历史管理、响应格式化、错误重试等。

因此,我选择了增加一个中间层——中转服务器。这个服务器部署在可以顺畅访问OpenAI的网络环境中(例如海外VPS)。Android App通过WebSocket与这个中转服务器建立长连接,所有的对话请求和AI回复都通过这个通道进行。服务器负责验证客户端身份、组装符合OpenAI API格式的请求、调用API、处理流式响应,并将其转发回客户端。

技术选型考量:

  • 通信协议:WebSocket (WSS)。对话场景天然适合长连接、双向通信。相比HTTP轮询,WebSocket能实现真正的低延迟、全双工通信,特别适合处理OpenAI API的流式响应(Server-Sent Events),服务器收到一个数据块就能立即推给客户端,实现打字机效果。
  • 服务端框架:Spring Boot。这是Java生态中最成熟、开发效率最高的Web框架之一,能快速搭建RESTful API和WebSocket端点。其丰富的生态(如Spring Security用于鉴权)和清晰的架构,便于后期维护和扩展。
  • 客户端网络库:OkHttp + WebSocket。OkHttp是Android上事实标准的网络库,其WebSocket实现稳定且功能完整,能够很好地处理连接生命周期、消息发送和接收。

2.2 数据流与安全设计

一次完整的对话交互,数据流是这样的:

  1. 用户在Android App的输入框键入消息并发送。
  2. App将消息、当前会话ID(用于关联对话记忆)等数据,通过已建立的WSS连接发送到中转服务器。
  3. 中转服务器收到请求后,首先进行基础验证(例如检查Token)。然后,它会从数据库或缓存中取出该会话ID对应的历史对话记录。
  4. 服务器将用户的新消息和历史记录组合,构造成OpenAI API要求的messages数组,并添加模型、温度等参数,向https://api.openai.com/v1/chat/completions发起HTTP请求,同时设置stream: true以开启流式传输。
  5. OpenAI的响应以SSE流的形式返回。中转服务器会实时读取这个流,每解析出一个完整的文本块(data chunk),就立即通过WebSocket转发给Android客户端。
  6. Android客户端收到文本块后,将其追加显示到UI的对话气泡中,形成“逐字打印”的效果。
  7. 当流结束时,服务器会将本轮完整的对话(用户消息+AI回复)持久化到数据库,更新该会话的历史记录,以便下次对话使用。

安全方面,我主要做了两层设计:

  1. 传输安全:全程使用WSS(WebSocket Secure),即基于TLS/SSL加密的WebSocket,保证数据传输过程中不被窃听或篡改。
  2. 认证安全:客户端与服务器连接时,需要携带一个预先分配或动态生成的Token。这个Token不直接是OpenAI API Key,而是我们服务自己的一套认证体系。API Key只保存在服务器环境变量或配置中心,绝不暴露给客户端。这样即使某个客户端Token泄露,也只会影响该客户端,我们可以快速吊销该Token,而无需更换昂贵的OpenAI API Key。

2.3 客户端保活与连接稳定性优化

移动网络环境复杂,Wi-Fi和蜂窝数据切换、设备休眠等都可能导致WebSocket连接意外断开。连接一旦断开,用户体验就会中断。为此,我实现了一套连接保活与重连机制。

心跳保活:客户端会定时(例如每30秒)向服务器发送一个特定的Ping消息(或空操作帧)。服务器收到后回复Pong。通过这种心跳机制,双方都能感知连接是否健康。如果连续多次发送心跳未收到回应,客户端就判断连接已失效。

智能重连:当检测到连接断开时,客户端不会立即无脑重连。我设计了一个带有退避策略的重连逻辑。例如,第一次断开后等待1秒重试,第二次失败后等待2秒,第三次等待4秒,以此类推,直到一个最大等待时间(如30秒)。这避免了在网络瞬时波动或服务器短暂压力大时,客户端海量的即时重连请求压垮服务器。

状态恢复:更关键的是,重连成功后,如何恢复之前的对话状态?我为此设计了会话(Session)机制。每次建立新连接或重连成功后,客户端会主动向服务器发送一个“同步状态”的请求,携带当前的会话ID。服务器会根据这个ID,将之前缓存的最近几条对话历史(或完整的上下文)重新发送给客户端,让UI界面恢复到断开前的样子,用户几乎无感知。

注意:心跳间隔不宜太短,否则会增加不必要的流量和服务器负担;也不宜太长,否则无法及时检测死连接。根据移动网络特性,20-40秒是一个比较平衡的区间。同时,服务器端也需要配置合理的WebSocket超时时间,应大于客户端的心跳间隔。

3. Android客户端核心实现详解

3.1 UI层:对话界面的实现

UI采用经典的RecyclerView来展示对话列表,每个条目是一个包含头像、文本内容的气泡。关键在于处理流式响应的UI更新。

当收到服务器推送来的一个文本块时,我们不能简单地替换整个回复,那样会导致界面闪烁。我的做法是:

  1. 在数据层,维护一个代表当前AI回复的StringBuilder
  2. 每次收到新文本块,就append到这个StringBuilder中。
  3. 通过HandlerLiveData将最新的完整文本(即StringBuilder.toString())通知到UI线程。
  4. UI线程中,找到RecyclerView中对应AI回复的那个ViewHolder,直接更新其文本内容。
// 简化的示例代码 class ChatAdapter : RecyclerView.Adapter<MessageViewHolder>() { val messages = mutableListOf<Message>() fun appendToLastAIMessage(newChunk: String) { if (messages.isNotEmpty() && messages.last().isFromAI) { val lastMsg = messages.last() lastMsg.content += newChunk // 更新数据源 notifyItemChanged(messages.size - 1) // 局部更新最后一个item } } }

为了提升体验,我还加入了消息复制功能(长按消息气泡弹出菜单)和清除上下文的按钮。清除上下文本质上是通知服务器丢弃当前会话ID关联的历史记录,并可能创建一个新的会话ID。

3.2 网络层:WebSocket连接管理

这是客户端的核心模块。我将其封装为一个单例类WebSocketManager,负责连接、发送、接收和重连的所有逻辑。

class WebSocketManager private constructor() { private var webSocket: WebSocket? = null private val okHttpClient = OkHttpClient.Builder() .pingInterval(30, TimeUnit.SECONDS) // 设置心跳,OkHttp会自动处理Ping/Pong帧 .build() fun connect(url: String, token: String) { val request = Request.Builder() .url("$url?token=$token") // Token可以通过URL参数或Header传递 .build() val listener = object : WebSocketListener() { override fun onOpen(webSocket: WebSocket, response: Response) { // 连接成功,更新状态,可能触发状态同步 } override fun onMessage(webSocket: WebSocket, text: String) { // 处理服务器推送的文本消息(JSON格式的对话数据块) parseAndHandleMessage(text) } override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { // 连接正常关闭 } override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { // 连接失败,触发重连逻辑 scheduleReconnect() } } webSocket = okHttpClient.newWebSocket(request, listener) } private fun scheduleReconnect() { // 实现带退避策略的重连定时器 } fun sendMessage(sessionId: String, content: String) { val json = JsonObject().apply { addProperty("type", "chat") addProperty("session_id", sessionId) addProperty("content", content) }.toString() webSocket?.send(json) } }

关键点:OkHttpClient的pingInterval设置非常有用,它确保了TCP层的心跳,有助于防止NAT超时导致连接被中间路由器断开。应用层的心跳(发送特定JSON指令)可以作为补充。

3.3 配置与模型管理

用户需要在App内进行一些必要配置。我设计了一个配置页面,主要包含:

  • 服务器地址:WebSocket服务器的WSS URL。
  • 认证Token:用于连接服务器的凭证。
  • API模型选择:下拉列表,让用户选择希望使用的模型,如gpt-3.5-turbogpt-4gpt-4o等。这个选择会通过WebSocket告知服务器,服务器在调用OpenAI API时使用对应的模型参数。
  • 上下文长度/最大Token数:这是一个高级设置,用于控制单次请求消耗的Token上限,直接影响对话记忆的长度和API费用。

这些配置使用Android的SharedPreferencesDataStore进行持久化。模型列表我建议在App内硬编码一个常用模型的列表,同时允许服务器在握手时动态下发支持的模型列表,这样更加灵活。

4. 服务端(Spring Boot中转)实现要点

4.1 WebSocket端点与消息分发

服务端使用Spring的@EnableWebSocketWebSocketHandler来建立WebSocket端点。

@Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new ChatWebSocketHandler(), "/ws/chat") .setAllowedOrigins("*"); // 生产环境应严格限制Origin } }

ChatWebSocketHandler中,需要处理连接建立、收到消息、连接关闭等事件。当收到客户端发来的聊天消息时,核心流程如下:

  1. 消息解析与验证:解析JSON,验证Token有效性,提取sessionId和用户消息。
  2. 上下文组装:根据sessionId从Redis或数据库中查询历史对话记录。将历史记录和新的用户消息组合成OpenAI API要求的格式。这里需要注意管理上下文长度,如果历史记录太长,需要采用某种策略(如只保留最近N轮,或滑动窗口)进行截断,以保证总Token数不超过模型限制和设置的最大值。
  3. 调用OpenAI API:使用Spring的WebClientRestTemplate,向OpenAI发起流式请求。关键是要正确设置HttpHeaders,包括Authorization: Bearer {apiKey}Accept: text/event-stream
  4. 流式响应处理与转发:读取OpenAI返回的SSE流,逐行解析。每解析出一个包含“delta.content”的完整数据块,就将其封装成自定义的JSON格式,通过WebSocketSession.sendMessage()方法实时发送给对应的客户端。
  5. 上下文持久化:流式响应结束后,将本轮完整的对话(用户消息+AI完整回复)存入历史记录,更新缓存或数据库。

4.2 上下文管理与会话状态

会话状态的管理至关重要,它决定了AI是否拥有“记忆”。我采用sessionId作为会话的唯一标识。这个ID可以由客户端在首次连接时生成并传递,也可以由服务器分配。

存储方案选择

  • 数据库(如MySQL):适合需要永久保存聊天记录的场景,但读写频繁时对数据库压力大。
  • 内存缓存(如Caffeine):速度极快,适合临时会话,但服务器重启数据丢失。
  • 分布式缓存(如Redis)这是我推荐的生产环境方案。它速度快,支持设置过期时间(例如会话闲置30分钟后自动清除),并且数据可以持久化,也适合多服务器实例共享会话状态。

在Redis中,可以用chat:session:{sessionId}这样的Key来存储一个列表(List)或字符串(JSON序列化后的整个上下文数组)。

4.3 性能、安全与扩展考量

性能优化

  • 连接池:用于HTTP客户端(如WebClient)调用OpenAI API,避免频繁创建连接的开销。
  • 响应式编程:使用WebClient的响应式模式处理SSE流,可以更高效地利用资源,避免阻塞线程。
  • 异步处理:WebSocket消息的处理、OpenAI API的调用、响应的转发,都应尽量采用异步非阻塞方式,避免占用WebSocket的工作线程太久。

安全加固

  • Origin检查:在WebSocketConfig中,不要使用setAllowedOrigins("*"),而应配置确切的客户端应用来源(如App的包名或域名)。
  • Token鉴权:实现一个简单的Token白名单或JWT验证机制。连接建立时验证,而不是每次消息都验证(但可以在关键操作时再次校验)。
  • 频率限制(Rate Limiting):基于IP或Token,对客户端的请求频率进行限制,防止恶意滥用导致API费用暴涨。可以使用Spring的@RateLimit注解或借助Redis实现滑动窗口计数。
  • 输入校验与清理:对客户端发送的消息内容进行基本的校验和清理,防止注入攻击(虽然主要是文本,但良好的习惯很重要)。

扩展性

  • 多模型支持:服务端可以维护一个模型路由配置。除了OpenAI,还可以接入其他大模型API(如国内的一些厂商)。客户端发送的请求中可以指定模型代号,服务端根据代号路由到不同的API提供商。
  • 插件机制:可以在消息发送给OpenAI前或收到响应后,插入一些处理插件,比如敏感词过滤、日志记录、响应内容格式化等。

5. 部署、配置与使用指南

5.1 服务端部署

  1. 环境准备:准备一台可以访问api.openai.com的海外服务器(VPS),安装JDK 17+、Maven/Gradle和Redis。
  2. 获取代码:从GitHub克隆ChatGPT_Server项目。
  3. 配置修改
    • application.yml中配置OpenAI API Key:openai.api-key=sk-your-key-here
    • 配置Redis连接信息。
    • 配置服务器监听的端口(如server.port=8080)。
    • 重要:生成一个或多个用于客户端认证的Token,可以硬编码在配置文件中,或者实现一个简单的从数据库读取的逻辑。
  4. 构建与运行
    mvn clean package java -jar target/chatgpt-server-0.0.1.jar
    建议使用systemdsupervisor等进程管理工具来守护进程,并配置SSL证书(可以使用Let‘s Encrypt的certbot)以启用WSS。Nginx可以作为反向代理,处理SSL终止并将/ws/路径的请求转发给Spring Boot应用。

5.2 Android客户端编译与配置

  1. 获取代码:克隆ChatGPT_Android项目,用Android Studio打开。
  2. 修改配置:找到存放服务器地址和Token的配置类或资源文件(如Constants.ktconfig.xml),将其中的示例值替换成你自己部署的服务器的WSS地址和分配的Token。
    // 示例 object Constants { const val WS_SERVER_URL = "wss://your-domain.com/ws/chat" const val DEFAULT_TOKEN = "your_client_token_here" }
  3. 编译运行:连接真机或模拟器,点击运行。首次进入App,请先到设置页面确认配置是否正确。

5.3 使用流程与技巧

  1. 启动与连接:打开App,如果配置正确,它会自动尝试连接服务器。你可以在界面看到连接状态指示。
  2. 开始对话:在底部的输入框输入问题,点击发送。你会看到消息出现在聊天区域,然后AI的回复会以“打字”的效果逐字出现。
  3. 管理对话
    • 连续对话:默认情况下,AI会记住当前会话中的所有历史记录。你可以进行多轮对话,上下文会自动传递。
    • 清除记忆:点击界面上的“清除上下文”或类似按钮。这实际上会通知服务器重置当前会话的历史记录,并可能开启一个新会话。当你想要开启一个全新话题时,这个操作非常必要,可以避免无关历史对AI回答的干扰。
    • 复制回复:长按AI的回复气泡,选择复制,方便你将内容粘贴到其他地方。
  4. 模型选择:在设置里,你可以尝试切换不同的模型。gpt-3.5-turbo性价比最高,响应快;gpt-4gpt-4o在复杂推理、创意写作上能力更强,但价格更贵、速度可能稍慢。服务器的API Key需要对应模型有权限。

实操心得:对于日常聊天和简单问答,gpt-3.5-turbo完全足够。只有在进行代码调试、复杂逻辑分析或需要更高一致性的长文本生成时,才考虑切换到GPT-4系列。同时,在服务器配置中,可以为不同模型设置不同的速率限制和成本警报。

6. 常见问题排查与优化建议

在实际开发和用户反馈中,我遇到了不少典型问题,这里做一个集中梳理。

6.1 连接类问题

问题1:App一直显示“连接中”或连接失败。

  • 排查步骤
    1. 检查服务器状态:首先确认你的Spring Boot服务端是否正常运行(systemctl status your-service),端口是否开放(netstat -tlnp | grep :8080)。
    2. 检查网络连通性:在服务器上curl https://api.openai.com,确认服务器本身能访问OpenAI。
    3. 检查SSL证书:如果使用WSS,确保域名SSL证书有效且被信任。可以用浏览器访问https://your-domain.com测试。
    4. 检查防火墙:确保服务器安全组/防火墙放行了WebSocket服务端口(如8080)和可能的SSL端口(443)。
    5. 检查客户端配置:确认App里配置的服务器地址(WSS URL)和Token完全正确,没有多余空格。
    6. 查看日志:检查服务端应用日志,看是否有连接错误、鉴权失败等信息。

问题2:连接经常无故断开。

  • 可能原因与解决
    • NAT超时:移动运营商或路由器会清理长时间空闲的TCP连接。解决:确保客户端和服务端都启用了心跳保活(Ping/Pong)。OkHttp的pingInterval和Spring的withSockJS()(或手动发送心跳帧)都能有效缓解此问题。
    • 服务器资源不足:服务器内存或CPU耗尽,导致进程被杀。解决:监控服务器资源,升级配置或优化应用。
    • 客户端网络切换:手机在Wi-Fi和移动数据间切换时IP会变。解决:客户端需要实现健壮的重连机制,并在网络变化监听器中主动触发重连检查。

6.2 对话与功能类问题

问题3:AI的回复出现乱码或显示异常。

  • 排查步骤
    1. 统一编码:确保服务端和客户端在整个数据流处理链(HTTP请求、WebSocket、JSON序列化/反序列化)中都使用UTF-8编码。
    2. 检查数据格式:服务端转发OpenAI的流式数据时,要确保解析正确,不要损坏或错误拼接了数据块。可以在服务端日志中打印出要转发给客户端的原始字符串,看是否包含不可见字符。
    3. 版本匹配:如项目README所述,确保App客户端和服务端代码版本兼容,特别是自定义的通讯协议标记(如果有)要一致。

问题4:对话没有记忆,每次都是新的开始。

  • 排查步骤
    1. 检查sessionId:确认客户端在每次发送消息时,是否传递了同一个有效的sessionId。可以在客户端发送数据和服务器接收数据时打印日志查看。
    2. 检查Redis/存储:登录服务器,用Redis-cli检查对应的Key(如chat:session:{yourSessionId})是否存在,里面的数据是否正确。可能是数据没有成功存储,或者过期时间设置得太短。
    3. 检查上下文组装逻辑:在服务端代码中,检查从存储中取出历史记录并组装到OpenAI API请求的代码逻辑是否正确,是否在请求结束后正确保存了新的上下文。

问题5:响应速度很慢。

  • 可能原因与优化
    • OpenAI API延迟:这是主要因素,尤其是GPT-4模型或高峰时段。优化:在服务端设置合理的API调用超时时间,并在客户端UI上给出“正在思考”的加载提示。
    • 服务器地理位置:中转服务器离OpenAI服务器或用户物理距离太远,网络延迟高。优化:选择网络链路优质的云服务商和机房位置。
    • 服务端处理瓶颈:检查服务端CPU和内存使用情况。如果处理并发请求多,考虑升级服务器配置,或者将一些操作(如历史记录读写)进一步异步化。

6.3 成本与安全类问题

问题6:API调用费用增长过快。

  • 控制策略
    • 设置使用限额:在服务端为每个Token或用户设置每日/每月调用次数或Token消耗上限。
    • 监控与告警:定期查看OpenAI后台的用量和费用统计。可以写一个简单的脚本,定时调用OpenAI的用量查询接口,当费用超过阈值时发送邮件或短信告警。
    • 优化上下文长度:在服务端代码中,严格控制每次请求携带的历史对话Token总数。对于长对话,采用只保留最近N轮或总结前文的方式,避免无限制增长。
    • 模型选择:引导用户在不必要时使用更经济的gpt-3.5-turbo模型。

问题7:如何防止他人滥用我的服务?

  • 加固措施
    • 强Token鉴权:不要使用简单的固定Token。可以实现一个Token申请/吊销系统,每个客户端使用独立的Token,并记录调用日志。
    • IP白名单:如果用户固定,可以在Nginx或Spring Security层面配置IP白名单,只允许特定的IP地址连接WebSocket。
    • 人机验证:在App启动或首次连接时,可以集成简单的验证码(如极验),虽然对用户体验有损,但能有效防止脚本滥用。
    • WebSocket路径混淆:不使用常见的/ws路径,可以改为一个无规律的路径,增加扫描难度。

开发这个项目的过程中,最大的体会就是“细节决定体验”。一个简单的对话功能,背后涉及到网络稳定性、状态管理、错误处理、成本控制等一系列工程问题。从最初只能单次问答,到实现流式响应和对话记忆,再到增加保活机制应对复杂的移动网络环境,每一步优化都让应用更加可靠和好用。如果你也想自己搭建一个,我的建议是先从最核心的“请求-响应”链路跑通,然后再逐步叠加流式、记忆、中转这些高级特性,每完成一步都充分测试,这样更容易定位和解决问题。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/7 12:05:06

告别编译噩梦:Mixly ESP32开发环境一键修复工具链头文件缺失问题

告别编译噩梦&#xff1a;Mixly ESP32开发环境一键修复工具链头文件缺失问题 如果你经常使用Mixly进行ESP32项目开发&#xff0c;一定遇到过这样的场景&#xff1a;重装系统、更换电脑或升级Mixly版本后&#xff0c;原本运行良好的项目突然无法编译&#xff0c;报错提示bits/cc…

作者头像 李华
网站建设 2026/5/7 12:04:49

高质量提示词仓库:AI交互效率提升与开源协作实践

1. 项目概述&#xff1a;一个高质量的提示词仓库在AI应用开发与日常使用中&#xff0c;无论是与大型语言模型&#xff08;LLM&#xff09;如ChatGPT、Claude对话&#xff0c;还是利用Midjourney、Stable Diffusion等工具进行图像生成&#xff0c;一个核心的共识是&#xff1a;提…

作者头像 李华
网站建设 2026/5/7 12:03:30

BilibiliDown:终极免费B站视频下载器,快速打造你的离线视频库

BilibiliDown&#xff1a;终极免费B站视频下载器&#xff0c;快速打造你的离线视频库 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader &#x1f633; 项目地址: https://gitcod…

作者头像 李华
网站建设 2026/5/7 11:56:56

别再写一堆UNION ALL了!用Spark SQL的Grouping Sets一次搞定多维聚合分析

用Spark SQL的Grouping Sets重构多维聚合分析&#xff1a;告别UNION ALL的繁琐时代 在数据仓库和BI报表开发中&#xff0c;分析师们经常需要从不同维度对数据进行聚合统计。传统做法是编写多个UNION ALL连接的查询&#xff0c;这不仅使代码冗长难维护&#xff0c;还影响执行效率…

作者头像 李华
网站建设 2026/5/7 11:56:24

C++ 读写 CSV 文件

1. CSV格式定义 逗号分隔值&#xff08;Comma-Separated Values&#xff0c;CSV&#xff0c;有时也称为字符分隔值&#xff0c;因为分隔字符也可以不是逗号&#xff09;&#xff0c;其文件以纯文本形式存储表格数据&#xff08;数字和文本&#xff09;。纯文本意味着该文件是一…

作者头像 李华