基于MongoDB实现聊天记录的存储

一、mongodb简介

1.1 mongodb简介

MongoDB是一个基于分布式文件存储的数据库,使用C++语言编写。它旨在为WEB应用提供可扩展的高性能数据存储解决方案。MongoDB介于关系数据库和非关系数据库之间,是非关系数据库当中功能最丰富、最像关系数据库的。

MongoDB将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB文档类似于JSON对象,字段值可以包含其他文档、数组及文档数组。它支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型。

MongoDB最大的特点是它支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

此外,MongoDB还具有以下特点:

  1. 面向集合存储,易存储对象类型的数据。
  2. 模式自由。
  3. 支持动态查询。
  4. 支持完全索引,包含内部对象。
  5. 支持查询。
  6. 支持复制和故障恢复。
  7. 使用高效的二进制数据存储,包括大型对象(如视频等)。
  8. 自动处理碎片,以支持云计算层次的扩展性。
  9. 可通过网络访问。

在高负载情况下,添加更多的节点可以保证服务器性能。MongoDB也易于部署和使用,存储数据非常方便。

总的来说,MongoDB是一个高性能、易部署、易使用的数据库系统,具有丰富的功能和特点,适用于各种规模的应用程序和场景。

1.2 mongodb利弊

优点:

  1. 灵活性:MongoDB采用文档存储方式,这意味着数据以键值对的形式存储在BSON(二进制JSON)格式中,这使得它能够存储复杂的数据类型,包括数组、嵌套文档等。这种灵活性使得MongoDB能够轻松地适应各种数据模型。
  2. 易扩展性:MongoDB支持自动分片,这使得它能够轻松地扩展到大量数据和复杂查询场景。通过添加更多的节点,MongoDB可以自动地将数据分布到不同的节点上,从而提高整体性能。
  3. 高性能:MongoDB使用内存映射机制,将数据暂时存储在内存中,提高了IO效率,MongoDB支持快速的读写操作,尤其适用于大规模数据和高并发场景。它还提供了多种查询方式,包括范围查询、排序、聚合等,这使得查询操作比传统的关系型数据库更加快速。
  4. 社区支持:MongoDB有一个活跃的开源社区,这意味着用户可以很容易地找到帮助和资源,以及最新的技术更新和最佳实践。

缺点:

  1. 缺乏事务支持:MongoDB不支持传统的事务处理特性,这意味着在处理多个文档或集合之间的复杂操作时可能会遇到问题。虽然MongoDB提供了乐观并发控制和文档级锁定来解决并发问题,但在需要完整的事务支持的场景下可能不够用。
  2. 复杂性:由于MongoDB的灵活性,它可能比传统的关系型数据库更复杂。对于初学者来说,可能需要更长的时间来学习和理解其数据模型和查询语言。
  3. 数据一致性:MongoDB采用最终一致性模型,而不是强一致性模型。这意味着在某些情况下,数据可能不会立即反映所有的更改。这对于需要强一致性的应用来说可能是一个问题。
  4. 磁盘空间占用:由于MongoDB使用文件存储数据,因此可能会占用大量的磁盘空间。特别是在高写入负载的情况下,由于数据文件的增长和收缩,可能会导致磁盘碎片的产生。
1.3 mongodb使用场景

MongoDB的使用场景非常广泛,包括以下几个方面:

  1. 内容管理和发布系统:MongoDB的灵活文档模型和高性能写入能力使其成为内容管理和发布系统的理想选择。它可以存储和检索各种类型的内容,如文章、图片、视频等。
  2. 个性化推荐系统:MongoDB可以存储和查询用户的个人偏好和行为数据,从而支持个性化推荐。通过使用MongoDB的高性能索引和聚合功能,可以快速地分析和提供个性化的推荐结果。
  3. 实时分析和大数据处理:MongoDB的分布式架构和高可扩展性使其非常适合实时分析和大数据处理任务。它可以处理大量的并发读写操作,并且支持复杂的查询和聚合操作。
  4. 时序数据管理:MongoDB的存储引擎和索引结构对时序数据的管理非常高效。它可以存储和查询大量的时间序列数据,如传感器数据、日志数据等。
  5. 实时数据分析和监控:MongoDB的副本集和分片功能可以实现实时数据分析和监控。它可以处理大量的并发写入操作,并提供实时的查询结果。
  6. 社交网络和协作平台:MongoDB的文档模型非常适合存储和查询社交网络和协作平台的数据。它可以存储用户的个人资料、关系图谱、消息等。
  7. 位置数据管理和地理信息系统:MongoDB的地理空间索引和查询功能使其成为管理位置数据和地理信息系统的理想选择。它可以存储和查询地理位置、地理边界、地理特征等数据。
  8. 游戏场景:使用MongoDB存储游戏用户信息,用户的装备、积分等直接以内嵌文档的形式存储,方便查询、更新。
  9. 物流场景:使用MongoDB存储订单信息,订单状态在运送过程中会不断更新,以MongoDB内嵌数组的形式来存储,一次查询就能将订单所有的变更读取出来。

总的来说,MongoDB适用于各种场景,从网站数据到大数据处理,再到社交网络和游戏等领域,它都表现出强大的灵活性和可扩展性。

1.4 mongodb存储聊天记录和mysql存储的抉择

选择使用MySQL还是MongoDB来存储聊天记录取决于具体需求和场景。以下是两者的一些比较:

MySQL

  • 结构化数据:适用于存储结构化数据,如聊天记录中的文本、时间戳等。
  • 事务处理:支持事务处理,可以保证数据的一致性和完整性。
  • 成熟度与社区支持:是一个成熟的关系型数据库管理系统,拥有庞大的用户基础和丰富的社区支持。
  • 查询优化:适合对复杂查询和性能要求较高的场景。

MongoDB

  • 非结构化数据:适用于存储非结构化数据,如图片、语音消息等。
  • 灵活性:具有灵活的数据模型,可以轻松处理聊天记录中的各种格式和结构。
  • 水平扩展性:适用于大规模数据的存储和管理,具有水平扩展性。
  • 实时性:适合需要实时处理和快速响应的场景,如实时聊天应用。

综上所述,如果聊天记录主要是结构化数据并且需要事务处理和复杂查询,MySQL可能是一个更好的选择。如果聊天记录包含大量非结构化数据并且需要水平扩展和实时处理能力,对事务的完整性要求不高对存取速度要求较高我建议使用新兴的nosql类型数据 MongoDB可能更适合。

二、业务场景

需求:我们的需求是实现一个与AI对话的聊天系统,大概分为两个部分,一个是会话,一个是聊天
我给大家放张图帮助理解(左边是会话,右边是聊天)
在这里插入图片描述

三、聊天记录的存储和查询

3.1 聊天记录数据集合的设计,可以理解为数据表

会话collection:

@Data
@Document(value = "agents_session")
public class AgentsSession implements Serializable {private static final long serialVersionUID = 198529858452480909L;private String id;private String agentId;/*** session id*/private String sessionId;/*** 发送者id*/private String senderCode;/*** 消息(当前会话组中最早的一次提问(也就是用户想AI提问))*/private String message;/*** 发送时间*/private String sendTime;/*** 是否删除*/private Boolean isDeleted;}

聊天记录collection:

@Data
@Document(value = "agents_chat_messages")
public class AgentsChatMessages implements Serializable {private static final long serialVersionUID = 823228953137629152L;private String id;/*** 会话id*/private String sessionId;/*** 消息内容*/private String message;/*** 接收状态*/private Integer receiveStatus;/*** 发送者id*/private String senderCode;/*** 接收者id*/private String recipientCode;/*** 发送时间*/private String sendTime;/*** 消息类型 文本、图片、文件、语音等*/private String messageType;/*** 消息内容汉字个数*/private Integer tokens;/*** 当前支持以下:* user: 表示用户* assistant: 表示对话助手*/private String role;/*** 是否已读*/private Boolean isRead;/*** 是否删除*/private Boolean isDeleted;/*** 问答对匹配id*/private String questionAnswerId;
}
3.2 聊天记录存取的实现

service实现

public interface ChatMessagesService {/*** 分页获取会话列表* @param dto* @return*/PageModel<AgentsSessionVO> queryAgentSessionPage(AgentsSessionDTO dto, PageRequestDTO page);/*** 通过会话id分页获取会话列表* @param dto* @return*/PageModel<AgentsChatMessagesVO> queryAgentsChatMessagesPage(AgentsChatMessagesDTO dto, PageRequestDTO page);/*** 保存会话和聊天* @param messagesDTO*/void saveSessionAndMessages(AgentsChatMessagesDTO messagesDTO);
}

实现类:

@Service
@Slf4j
public class ChatMessagesServiceImpl implements ChatMessagesService {@Resourceprivate MongoTemplate mongoTemplate;/*** 获取会话列表** @param dto* @return*/@Overridepublic PageModel<AgentsSessionVO> queryAgentSessionPage(AgentsSessionDTO dto, PageRequestDTO page) {try {// 创建分页对象Pageable pageable = PageRequest.of(page.getPage() - 1, page.getSize(), Sort.Direction.DESC, "sendTime"); // 注意:页码从0开始,所以需要减1// 创建查询对象Query query = new Query();query.addCriteria(Criteria.where("senderCode").is(dto.getUserCode()).and("isDeleted").is(false));//设置模糊查询if (StringUtils.isNotEmpty(dto.getMessage())) {query.addCriteria(Criteria.where("message").regex(dto.getMessage()));}if (!CollectionUtils.isEmpty(dto.getAgentsIds())) {// in 条件查询Criteria criteria = Criteria.where("agentId").in(dto.getAgentsIds());query.addCriteria(criteria);}// 排序query.with(Sort.by(Sort.Order.desc("sendTime")));// 设置分页query.with(pageable);List<AgentsSessionVO> list = mongoTemplate.find(query, AgentsSessionVO.class, CommonConstant.AGENTS_SESSION);list.forEach(s ->{try {s.setMessage(AesEncryptionUtil.decrypt(s.getMessage()));} catch (Exception e) {throw new HxyAgentsXException("数据加载失败", e);}});long count = mongoTemplate.count(query, AgentsSessionVO.class, CommonConstant.AGENTS_SESSION);return new PageModel<AgentsSessionVO>(list, count, pageable);} catch (Exception e) {log.error("获取会话列表异常");throw new HxyAgentsXException("获取会话列表异常", e);}}/*** 通过会话id分页获取聊天记录** @param dto* @return*/@Overridepublic PageModel<AgentsChatMessagesVO> queryAgentsChatMessagesPage(AgentsChatMessagesDTO dto, PageRequestDTO page) {try {// 创建分页对象Pageable pageable = PageRequest.of(page.getPage() - 1, page.getSize(), Sort.Direction.ASC,"sendTime"); // 注意:页码从0开始,所以需要减1// 创建查询对象Query query = new Query();//设置模糊查询if (StringUtils.isNotEmpty(dto.getMessage())) {query.addCriteria(Criteria.where("message").regex(dto.getMessage()));}query.addCriteria(Criteria.where("sessionId").is(dto.getSessionId()).and("isDeleted").is(false));query.addCriteria(new Criteria().orOperator(Criteria.where("senderCode").is(dto.getUserCode()),Criteria.where("recipientCode").is(dto.getUserCode())));// 排序query.with(Sort.by(Sort.Order.asc("sendTime")));// 设置分页query.with(pageable);List<AgentsChatMessagesVO> list = mongoTemplate.find(query, AgentsChatMessagesVO.class, CommonConstant.AGENTS_CHAT_MESSAGES);list.forEach(m ->{try {m.setMessage(AesEncryptionUtil.decrypt(m.getMessage()));} catch (Exception e) {throw new HxyAgentsXException("数据加载失败", e);}});long count = mongoTemplate.count(query, AgentsChatMessagesVO.class, CommonConstant.AGENTS_CHAT_MESSAGES);return new PageModel<AgentsChatMessagesVO>(list, count, pageable);} catch (Exception e) {log.error("获取聊天记录列表异常");throw new HxyAgentsXException("获取聊天记录列表异常", e);}}/*** 存会话聊天* @param messagesDTO*/@Overridepublic void saveSessionAndMessages(AgentsChatMessagesDTO messagesDTO) {try {Criteria.where("sessionId").is(messagesDTO.getSessionId());AgentsSession agentsSessionOne = mongoTemplate.findOne(new Query(Criteria.where("sessionId").is(messagesDTO.getSessionId()).and("isDeleted").is(false)), AgentsSession.class);// 会话if (agentsSessionOne == null){AgentsSession agentsSession = new AgentsSession();agentsSession.setId(UUIDUtils.getUUID());agentsSession.setSessionId(messagesDTO.getSessionId());agentsSession.setAgentId(messagesDTO.getAgentId());agentsSession.setMessage(AesEncryptionUtil.encrypt(messagesDTO.getMessage()));agentsSession.setSenderCode(messagesDTO.getSenderCode());agentsSession.setIsDeleted(false);agentsSession.setSendTime(LocalDateUtil.localDateTimeToString(LocalDateUtil.getLocalDateTime(),"yyyy-MM-dd HH:mm:ss"));mongoTemplate.insert(agentsSession);}// 聊天AgentsChatMessages agentsChatMessages = new AgentsChatMessages();agentsChatMessages.setId(UUIDUtils.getUUID());agentsChatMessages.setSessionId(messagesDTO.getSessionId());agentsChatMessages.setMessage(AesEncryptionUtil.encrypt(messagesDTO.getMessage()));agentsChatMessages.setMessageType("text");agentsChatMessages.setRole(messagesDTO.getRole());agentsChatMessages.setIsRead(true);agentsChatMessages.setIsDeleted(false);agentsChatMessages.setSenderCode(messagesDTO.getSenderCode());agentsChatMessages.setRecipientCode(messagesDTO.getRecipientCode());agentsChatMessages.setSendTime(LocalDateUtil.localDateTimeToString(LocalDateUtil.getLocalDateTime(),"yyyy-MM-dd HH:mm:ss"));agentsChatMessages.setQuestionAnswerId(messagesDTO.getQuestionAnswerId());mongoTemplate.insert(agentsChatMessages);} catch (Exception e) {log.error("保存会话聊天失败",e);throw new HxyAgentsXException("保存会话聊天失败",e);}}
}

聊天内容加密:

public class AesEncryptionUtil {private static final String ALGORITHM = "AES";private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";private static final byte[] keyValue = "yourSecretKey".getBytes(StandardCharsets.UTF_8);public static String encrypt(String valueToEncrypt) throws Exception {SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);Cipher cipher = Cipher.getInstance(TRANSFORMATION);cipher.init(Cipher.ENCRYPT_MODE, key);byte[] encryptedByteValue = cipher.doFinal(valueToEncrypt.getBytes(StandardCharsets.UTF_8));return Base64.getEncoder().encodeToString(encryptedByteValue);}public static String decrypt(String encryptedValue) throws Exception {SecretKeySpec key = new SecretKeySpec(keyValue, ALGORITHM);Cipher cipher = Cipher.getInstance(TRANSFORMATION);cipher.init(Cipher.DECRYPT_MODE, key);byte[] originalValue = cipher.doFinal(Base64.getDecoder().decode(encryptedValue));return new String(originalValue, StandardCharsets.UTF_8);}public static void main(String[] args) throws NoSuchAlgorithmException {// 创建AES密钥生成器KeyGenerator keyGenerator = KeyGenerator.getInstance("AES");// 设置密钥长度为256位keyGenerator.init(128);// 生成密钥SecretKey secretKey = keyGenerator.generateKey();// 将密钥转换为字符串String keyString = Base64.getEncoder().encodeToString(secretKey.getEncoded());System.out.println("Generated AES key (Base64): " + keyString);}
}
3.3 保存问答聊天

在用户问答的时候保存聊天内容
在这里插入图片描述

在模型回答结束的时候保存聊天内容
在这里插入图片描述
最后大家可以结合自己的业务来实现聊天记录的存取。

最后送大家一句话白驹过隙,沧海桑田

文末送福利啦~

1、Java(SE、JVM)、算法数据结构、数据库(Mysql、redis)、Maven、Netty、RocketMq、Zookeeper、多线程、IO、SSM、Git、Linux、Docker、Web前端相关学习笔记
2、2023最新BATJ大厂面试题集
3、项目源码
4、学习小组
领取方式:关注下方公主号,回复:【笔记】、【面试】获取相关福利。

文章持续更新,可以关注下方公众号或者微信搜一搜「 迷迭香编程 」获取项目源码、干货笔记、面试题集,第一时间阅读,获取更完整的链路资料。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/249287.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#用正则表达式判断字符串是否纯数字vs用Char.IsDigit 方法遍历字符数组是否纯数字

目录 一、使用的方法 1.正则表达式 2.Char.IsDigit 方法 二、源码 1.源代码 2.生成效果 一、使用的方法 1.正则表达式 在程序运行过程中&#xff0c;经常需要用户输入数字信息&#xff0c;如输入员工年龄、工资等。使用正则表达式Regex类的IsMatch方法&#xff0c;可以有…

【ASP.NET Core 基础知识】--Web API--创建和配置Web API(一)

一、简介 Web API&#xff08;Web Application Programming Interface&#xff09;的重要性在于其在现代软件开发中扮演着关键的角色。以下是一些关于Web API重要性的方面&#xff1a; 跨平台交互&#xff1a; Web API允许不同平台、不同技术栈的应用程序进行通信。无论是Web…

如何本地搭建Emby影音管理服务并结合内网穿透实现远程访问本地影音库

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

负载均衡下的webshell连接

一、环境配置 1.在Ubuntu上配置docker环境 我们选择用Xshell来将环境资源上传到Ubuntu虚拟机上&#xff08;比较简单&#xff09; 我们选择在root模式下进行环境配置&#xff0c;先将资源文件复制到root下&#xff08;如果你一开始就传输到root下就不用理会这个&#xff09; …

微分几何——梅向明第四版学习笔记(一) 向量函数和曲线论

目录 引出向量函数曲线论简单曲线定义曲线的向量参数表示 曲线的切线【重要】曲线的法面【重要】曲线的自然参数表示 空间曲线曲线的密切平面空间曲线的基本三棱形【重要】单位切向量主法向量副法向量Frenet标架螺旋线的案例 曲线的曲率和曲率半径曲率的几何意义 曲线的挠率挠率…

顺序表与链表,栈与队列

名词辨析&#xff1a;指针 1.什么是指针&#xff0c;想必大家都不陌生&#xff0c;但是&#xff0c;在这部分的知识中&#xff0c;包含着一类特殊的指针&#xff0c;表面上它只是单个的数字&#xff0c;但它其实代表了作为栈或者队列载体的数组的下标&#xff0c;在实际题目中…

Golang语言异常机制解析:错误策略与优雅处理

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站https://www.captainbed.cn/kitie。 前言 作为开发者来说&#xff0c;我们没办法保证程序在运行过程中永远不会出现异常&#xff0c;对于异常…

Java多线程编程中的异常处理策略

第1章&#xff1a;引言 大家好&#xff0c;我是小黑&#xff0c;咱们今天聊聊异常处理。想必大家在写代码的时候都遇到过各种各样的异常吧&#xff1f;有时候&#xff0c;一个小小的异常如果处理不当&#xff0c;就可能导致整个程序崩溃。特别是在多线程环境下&#xff0c;异常…

科技云报道:云原生PaaS,如何让金融业数字化开出“繁花”?

科技云报道原创。 在中国金融业数字化转型的历史长卷中&#xff0c;过去十年无疑是一部磅礴的史诗。 2017年&#xff0c;南京银行第一次将传统线下金融业务搬到了线上。那一年&#xff0c;它的互联网金融信贷业务实现了过去10年的业务总额。 2021年&#xff0c;富滇银行通过…

幻兽帕鲁服务器游戏怎么升级版本?

幻兽帕鲁服务器游戏怎么升级版本&#xff1f;自建幻兽帕鲁服务器进入Palworld游戏提示“您正尝试加入的比赛正在运行不兼容的游戏版本&#xff0c;请尝试升级游戏版本”什么原因&#xff1f;这是由于你的客户端和幻兽帕鲁服务器版本不匹配&#xff0c;如何解决&#xff1f;更新…

配置IPv6静态路由

1、静态路由简介 静态路由是一种需要管理员手工配置的特殊路由。 静态路由在不同网络环境中有不同的目的&#xff1a; 当网络结构比较简单时&#xff0c;只需配置静态路由就可以使网络正常工作。 在复杂网络环境中&#xff0c;配置静态路由可以改进网络的性能&#xff0c;并…

Synchronized作用

synchronized能够在同一时刻最多只有一个线程执行该代码 证明如下&#xff1a; public class MyThread {public static void main(String[] args) throws InterruptedException {Ticket ticket new Ticket();Thread aa new Thread(() -> {try {ticket.getCount();} catc…

性能测试工具架构

背景 性能测试工具&#xff08;LoadRunner为例&#xff09; 性能测试工具通常是指那些用来支持压力、负载测试&#xff0c;能够录制和生成脚本、设置和部署场景、产生并发用户和向系统施加持续压力的工具。 性能测试工具录制的是服务端与应用之间的通信数据&#xff0c;而不是…

怎么进行视频压缩大小?常见的4种压缩方法

在当今数字化的时代&#xff0c;我们经常处理大量的视频文件&#xff0c;无论是用于社交媒体分享、视频制作还是存储在我们的设备中。然而&#xff0c;随着视频质量的提升和分辨率的增加&#xff0c;视频文件的大小也相应地变得更加庞大&#xff0c;给存储、分享和传输带来了一…

hal库stm32串口接收不定长数据

参考博客&#xff1a; https://blog.csdn.net/qq_41830158/article/details/121254705 按下面步骤修改实测可用 步骤&#xff1a; 添加串口接收所需变量   打开uart.c文件&#xff0c;在文件顶部的USER CODE BEGIN 0下方添加下列变量 volatile uint8_t rx1_len 0; //接收…

友思特应用 | 微观指尖世界:OCT成像应用之3D指纹提取与识别

欢迎访问官网&#xff0c;探索丰富案例&#xff1a; OCT成像系统 | 光学相干断层扫描 | 谱域OCT | 扫频OCT | 广州友思特科技有限公司 关注“友思特机器视觉与光电”公众号、加入行业交流群或直接联系我们&#xff0c;轻松收获更多技术干货 导读 数字化生活已离不开指纹识别认…

《区块链简易速速上手小册》第6章:区块链在金融服务领域的应用(2024 最新版)

文章目录 6.1 金融服务中的区块链6.1.1 金融服务中区块链的基础6.1.2 主要案例&#xff1a;跨境支付6.1.3 拓展案例 1&#xff1a;去中心化金融&#xff08;DeFi&#xff09;6.1.4 拓展案例 2&#xff1a;代币化资产 6.2 区块链在支付系统中的作用6.2.1 支付系统中区块链的基础…

小程序定制开发前,应该考虑些什么?

引言 在移动互联网时代&#xff0c;小程序已经成为许多企业和个人推广业务、提供服务的理想平台。然而&#xff0c;在进行小程序定制开发之前&#xff0c;开发者和业务方需要细致入微地考虑一系列关键因素&#xff0c;以确保最终的小程序既能满足用户需求&#xff0c;又能够顺…

arcgis 批量删除字段

一、打开ArcToolbox-数据管理工具-字段-删除字段。 二、在输入表中选择要删除字段的要素&#xff0c;在删除字段栏中选择要删除的字段&#xff0c;点击确认即可。

Java强训day13(选择题编程题)

选择题 编程题 题目1 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);String s sc.nextLine();char[] c s.toCharArray();int i 0;int t 0;while (i < c.length) {if (c[i] ! \") {…