基于tcp协议及数据库sqlite3的云词典项目

这个小项目是一个网络编程学习过程中一个阶段性检测项目,过程中可以有效检测我们对于服务器客户端搭建的能力,以及一些bug查找能力。项目的一个简单讲解我发在了b站上,没啥心得,多练就好。
https://t.bilibili.com/865244702526406675?share_source=pc_native

数据库创建及导入单词表

#include <stdio.h>
#include <sqlite3.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{// 1.打开或创建数据库
    sqlite3 *db = NULL;int rc;if (sqlite3_open("./word.db", &db) != SQLITE_OK)//打开或创建库{printf("sqlite3_open err:%s\n", sqlite3_errmsg(db));return -1;}printf("sqlite3_open success\n");// 2.创建表char *errmsg = NULL;//返回创建数据库表的错误//创建一个两列的单词表,用于存储单词及注释if (sqlite3_exec(db, "create table if not exists wd1 (word char, annotation char);", NULL, NULL, &errmsg) != SQLITE_OK){printf("create err: %sn", errmsg);sqlite3_close(db);  return -1;}//创建一个两列的账户表,用于存储账户名及对应密码if (sqlite3_exec(db, "create table if not exists user (name char, password char);", NULL, NULL, &errmsg) != SQLITE_OK){printf("create err: %sn", errmsg);sqlite3_close(db);return -1;}printf("create success\n");// 3.向表中插入数据
    FILE *fp = fopen(argv[1], "r");//打开要插入的文件流if (fp == NULL){printf("failed to open file\n");sqlite3_close(db);return -1;}char buf[1024];  //读取的一行char word[32];   //单词char ant[1024];  //保存注释while (fgets(buf, sizeof(buf), fp) != NULL) //读一行{sscanf(buf, "%99[^ ] %256[^\n]", word, ant);  //将第一个单词放到单词数组中,后面的内容放到注释数组中char sql[1024];//存放命令内容sprintf(sql, "insert into wd1 values(\"%s\", \"%s\");", word, ant);  // 构造插入语句,将单词及注释插入到单词表中
        rc = sqlite3_exec(db, sql, NULL, NULL, &errmsg);if (rc != SQLITE_OK){printf("insert err: %s\n", errmsg);return -1;}}fclose(fp);//关闭文件描述符// 5.关闭数据库连接sqlite3_close(db);return 0;
}

头函数及传输协议

#ifndef __HEAD_H__
#define __HEAD_H__//防止重包含
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#define N 32
enum type_t //运行命令
{
    R=4, //register注册
    L, //login登录
    Q, //query搜索
    H, //history历史
};
typedef struct //数据包结构体
{
    int type;//执行命令类型
    char name[N];   //用户名
    char data[1024]; //密码或要查询的单词
} MSG_t;
typedef struct node_t
{
    struct sockaddr_in addr; //ip地址
    struct node_t *next;     //链表下一个地址
} list_t;
#endif

云词典服务器端

/*服务器创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sqlite3.h>
#include <signal.h>
#include <sys/select.h>
#include <time.h>
#include <sys/time.h>
#include "head.h"
MSG_t msg;
int n;
sqlite3 *db = NULL;  //命令输入
char *errmsg = NULL; //错误码
int hang, lie;       //数据库行和列
int k = 0;
int Register(sqlite3 *db, int sockfd) //注册函数
{
    char **result = NULL; //数据库返回内容
    //3.向表中插入数据
    //(1)执行函数
    char sq1[128];                                                     //保存命令
    sprintf(sq1, "select * from user where name = \"%s\";", msg.name); 
    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg);//判断数据库中是否已经存在该账户
    if (hang != 0)
    {
        sprintf(msg.data, "账户已存在;\n");
        send(sockfd, &msg, sizeof(msg), 0);
        return -1;
    }
    else //成功注册
    {
        sprintf(sq1, "insert into user values(\"%s\",\"%s\");", msg.name, msg.data);
        if (sqlite3_exec(db, sq1, NULL, NULL, &errmsg) == SQLITE_OK) //""需要用\转意,注册成功插入用户表内
        {
            sprintf(sq1, "create table if not exists \"%s\" (word char, time char);", msg.name); //每注册一个用户创建一个新表
            if (sqlite3_exec(db, sq1, NULL, NULL, &errmsg) != SQLITE_OK)                         //创建新表
            {
                printf("create err: %s", errmsg);
                sqlite3_close(db);
                return -1;
            }
            else
            {
                printf("creat %s success\n", msg.name);
            }
            sprintf(msg.data, "OK");
            send(sockfd, &msg, sizeof(msg), 0); //注册成功发送消息
            memset(msg.data, 0, sizeof(msg.data));
            return 0;
        }
        else //否则插入失败
        {
            printf("insert value err;%s\n", errmsg);
            return -1;
        }
    }
}
//用户登录
int loginclient(sqlite3 *db, int sockfd)
{
    char **result = NULL; //数据库返回内容
    char sq1[128];        //保存命令
    sprintf(sq1, "select * from user where name = \"%s\";", msg.name);
    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg); //判断数据库中是否已经存在该账户
    if (hang != 0)                                             //如果能读出内容则行数不为0
    {
        if (strcmp(result[3], msg.data) == 0) //判断密码是否正确
        {
            sprintf(msg.data, "OK");
            send(sockfd, &msg, sizeof(msg), 0);
            return 0;
        }
        else //密码错误
        {
            sprintf(msg.data, "password err\n");
            send(sockfd, &msg, sizeof(msg), 0);
            return -1;
        }
    }
    else //反之未注册
    {
        sprintf(msg.data, "no register\n"); //未注册
        send(sockfd, &msg, sizeof(msg), 0);
        return -1;
    }
}
//查询单词注释
int chatclient(sqlite3 *db, int sockfd) //查询单词函数
{
    char **result = NULL; //数据库返回内容
    char sq1[128];        //保存命令
    sprintf(sq1, "select * from wd1 where word = \"%s\";", msg.data);
    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg); //判断是否查到该单词
    if (hang != 0)
    {
        sprintf(msg.data, "%s", result[3]); //将注释内容发送到客户端
        send(sockfd, &msg, sizeof(msg), 0); //发送该单词注释
        time_t th;
        time(&th); //获取当前时间
        char times[128];
        struct tm *ts;
        ts = localtime(&th); //将当时间转化为标准时间
        sprintf(times, "%4d-%2d-%2d %2d:%2d:%2d", ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday, ts->tm_hour, ts->tm_min, ts->tm_sec);
        //先将时间放到一个字符串中,再放到命令语句中
        sprintf(sq1, "insert into \"%s\" values(\"%s\", \"%s\");", msg.name, result[2], times); // 构造插入语句
        int rc = sqlite3_exec(db, sq1, NULL, NULL, &errmsg);                                    //将查询时间保存到数据库中
        if (rc != SQLITE_OK)
        {
            printf("insert err: %s\n", errmsg);
            return -1;
        }
        return 0;
    }
    else //未找到该单词
    {
        sprintf(msg.data, "word unfund\n");
        send(sockfd, &msg, sizeof(msg), 0);
        return -1;
    }
}
int history(sqlite3 *db, int sockfd) //查询历史记录
{
    char **result = NULL;                            //数据库返回内容
    char sq1[128];                                   //保存命令
    sprintf(sq1, "select * from \"%s\";", msg.name); //查询单词查询历史
    sqlite3_get_table(db, sq1, &result, &hang, &lie, &errmsg);
    if (hang != 0)
    {
        for (int j = 0; j < hang; j++) //拼接表内内容到字符数组中
        {
            strcat(msg.data, result[j * lie + 2]);
            strcat(msg.data, " "); //两个表内内容间添加空格间隔
            strcat(msg.data, result[j * lie + 3]);
            strcat(msg.data, "\n"); //两行间换行
        }
        send(sockfd, &msg, sizeof(msg), 0); //发送该单词查询结果
        return 0;
    }
    else if (hang == 0) //历史记录为空
    {
        sprintf(msg.data, "history is void");
        send(sockfd, &msg, sizeof(msg), 0); //发送该单词查询结果
        return 0;
    }
}
int main(int argc, char const *argv[])
{
    // 1.打开或创建数据库
    int rc;
    if (sqlite3_open("./word.db", &db) != SQLITE_OK)
    {
        printf("sqlite3_open err:%s\n", sqlite3_errmsg(db));
        return -1;
    }
    printf("sqlite3_open success\n"); //打开数据库成功
    if (argc < 2)                     //行传参正确
    {
        printf("plase input <file><port>\n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    int acceptfd; //接收套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) //容错
    {
        perror("socket err");
        return -1;
    }
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;                   //协议族ipv4
    saddr.sin_port = htons(atoi(argv[1]));        //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    saddr.sin_addr.s_addr = inet_addr("0.0.0.0"); //ip地址,转化为16进制表示
    socklen_t len = sizeof(saddr);                //结构体大小
    //bind绑定ip和端口
    if (bind(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("bind err");
        return -1;
    }
    //3.启动监听,把主动套接子变为被动套接字
    if (listen(sockfd, 6) < 0)
    {
        perror("listen err");
        return -1;
    }
    //4.创建表
    fd_set readfds;    //原表
    fd_set tempfds;    //创建一个临时表,用来保存新表
    FD_ZERO(&readfds); //原表置空
    //5.填表
    FD_SET(sockfd, &readfds); //将要监测的文件描述符插入到表中
    int maxfd = sockfd;       //表内最大描述符
    int ret;
    //6.循环监听 select
    while (1)
    {
        tempfds = readfds;                                       //每次循环前重新赋值一次
        int ret = select(maxfd + 1, &tempfds, NULL, NULL, NULL); //监测
        if (ret < 0)
        {
            perror("select err");
            return -1;
        }
        if (FD_ISSET(sockfd, &tempfds)) //监听是否有客户端链接
        {
            //阻塞等待客户端的链接请求
            acceptfd = accept(sockfd, (struct sockaddr *)&saddr, &len);
            //获取客户端的ip和端口,(struct sockaddr *)&saddr:用来存放返回的ip,和端口
            if (acceptfd < 0)
            {
                perror("accept err");
                return -1;
            }
            printf("client ip:%s ,port:%d:connect success\n", inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port));
            //打印已经接入的客户端IP
            FD_SET(acceptfd, &readfds); //将新接入的客户端文件描述符插入到原表中
            if (acceptfd > maxfd)       //如果新插入的文件描述符大于已知最大文件描述符,则更新表内最大文件描述符
            {
                maxfd = acceptfd;
            }
        }
        for (int i = 5; i <= maxfd; i++) //遍历判断是否有信号传输
        {
            if (FD_ISSET(i, &tempfds)) //监测客户端文件描述符
            {
                int ret = recv(i, &msg, sizeof(msg), 0); //接收的信号
                if (ret < 0)                             //接收错误
                {
                    perror("recv err.");
                    return -1;
                }
                else if (ret == 0)
                {
                    printf("%d client exit\n", i); //客户端退出
                    close(i);                      //关闭描述符
                    FD_CLR(i, &readfds);           //删除文件描述符
                }
                else
                {
                    switch (msg.type)
                    {
                    case R: //注册
                        Register(db, i);
                        break;
                    case L: //登录
                        loginclient(db, i);
                        break;
                    case Q: //搜索
                        chatclient(db, i);
                        break;
                    case H: //历史
                        history(db, i);
                        break;
                    default:
                        break;
                    }
                }
            }
        }
    }
    close(sockfd);
    close(acceptfd);
    return 0;
}

云词典客户端

/*客户端创建代码 */
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h> /* superset of previous */
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "head.h"
#define N 32
MSG_t msg;
int n; //命令输入
//注册操作
void do_register(int sockfd)
{
    while (1)
    {
        msg.type = R; //注册状态
        printf("请输入您的用户名:");
        scanf("%s", msg.name);
        getchar();                         //回收一个垃圾字符
        if (memcmp(msg.name, "#", 1) == 0) //注册过程中输入#退出注册页面
        {
            printf("退出注册\n");
            break;
        }
        printf("请输入您的密码:");
        scanf("%s", msg.data);
        getchar();
        send(sockfd, &msg, sizeof(msg), 0);
        recv(sockfd, &msg, sizeof(msg), 0); //接收客户端消息,判断是否注册成功
        printf("register:%s\n", msg.data);
        if (memcmp(msg.data, "OK", 2) == 0) //服务器返回的msg.data == OK注册成功
        {
            break;
        }
    }
}
//登录操作
int do_login(int sockfd)
{
    while (1)
    {
        msg.type = L; //登录状态
        printf("请输入您的用户名:");
        scanf("%s", msg.name);
        getchar();
         if (memcmp(msg.data, "#", 1) == 0) //退出查询页面,先判断退出后发送内容
        {
            printf("退出查询\n");
            msg.type = 0;//当发送给服务器为结束内容,让服务器端读出且无法识别type,继续等待事件
            memset(msg.data,0,sizeof(msg.data));//将msg.data中的#清空
            send(sockfd, &msg, sizeof(msg), 0);
            break;
        }
        printf("请输入您的密码:");
        scanf("%s", msg.data);
        getchar();
        send(sockfd, &msg, sizeof(msg), 0);
        recv(sockfd, &msg, sizeof(msg), 0);
        printf("login:%s\n", msg.data);
        if (memcmp(msg.data, "OK", 2) == 0) //服务器返回的msg.data=OK登录成功
        {
            return 1;
        }
    }
}
//查询操作
void do_query(int sockfd)
{
    msg.type = Q;
    while (1)
    {
        printf("请输入你要查询的单词内容:");
        scanf("%s", msg.data);
        getchar();
        if (memcmp(msg.data, "#", 1) == 0) //退出查询页面,先判断退出后发送内容
        {
            printf("退出查询\n");
            msg.type = 0;//当发送给服务器为结束内容,让服务器端读出且无法识别type,继续等待事件
            memset(msg.data,0,sizeof(msg.data));
            send(sockfd, &msg, sizeof(msg), 0);
            break;
        }
        send(sockfd, &msg, sizeof(msg), 0);
        recv(sockfd, &msg, sizeof(msg), 0);
        printf("annotation:%s\n", msg.data); //打印接收的信息/注释
    }
}
//查询历史记录
void do_history(int sockfd)
{
    // memset(msg.data, 0, sizeof(msg.data));
    msg.type = H;
    send(sockfd, &msg, sizeof(msg), 0);
    recv(sockfd, &msg, sizeof(msg), 0); //接收信息,接一次打一次.
    printf("%s\n", msg.data);
}
//主函数
int main(int argc, char const *argv[])
{
    if (argc < 3)
    {
        printf("plase input <ip><port>\n");
        return -1;
    }
    //1.创建套接字,用于链接
    int sockfd;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0)
    {
        perror("socket err");
        return -1;
    }
    //文件描述符 0 -> 标准输入  1->标准输出  2->标准出错  3->socket
    printf("sockfd:%d\n", sockfd);
    //2.绑定 ip+port 填充结构体
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;                 //协议族ipv4
    saddr.sin_port = htons(atoi(argv[2]));      //端口号,htons将无符号短整数hostshort从主机字节顺序到网络字节顺序。
    saddr.sin_addr.s_addr = inet_addr(argv[1]); //ip地址,转化为16进制表示
    socklen_t len = sizeof(saddr);              //结构体大小
    //3用于连接服务器;
    if (connect(sockfd, (struct sockaddr *)&saddr, len) < 0)
    {
        perror("connect err");
        return -1;
    }
    int flag = 0;
    while (1)
    {
        printf("*******************************************************\n");
        printf("*                      <BUG词典>                       *\n");
        printf("*               1: 注册  2: 登录  3:  退出              *\n");
        printf("*******************************************************\n");
        printf("请输入命令:");
        scanf("%d", &n);
        getchar();
        switch (n)
        {
        case 1:
            do_register(sockfd);
            break;
        case 2:
            if (do_login(sockfd) == 1)
            {
                while (1)
                {
                    if (flag == 1)
                    {
                        flag = 0;
                        break;
                    }
                    printf("*******************************************************\n");
                    printf("*                      <BUG词典>                       *\n");
                    printf("*              1: 查询单词  2: 历史记录 3:  退出          *\n");
                    printf("*******************************************************\n");
                    printf("请输入命令:");
                    scanf("%d", &n);
                    getchar();
                    switch (n)
                    {
                    case 1:
                        do_query(sockfd);
                        break;
                    case 2:
                        printf("search history\n");
                        do_history(sockfd);
                        break;
                    case 3:
                        flag = 1;
                        break;
                    default:
                        break;
                    }
                }
            }
            break;
        case 3:
            return 0;
        default:
            break;
        }
    }
    close(sockfd);
    return 0;
}

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

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

相关文章

Shell判断:模式匹配:case(二)

简单的JumpServer 1、需求&#xff1a;工作中&#xff0c;我们需要管理N多个服务器。那么访问服务器就是一件繁琐的事情。通过shell编程&#xff0c;编写跳板程序。当我们需要访问服务器时&#xff0c;看一眼服务器列表名&#xff0c;按一下数字&#xff0c;就登录成功了。 2、…

JAVA毕业设计111—基于Java+Springboot+Vue的养老院管理系统(源码+数据库+12000字论文)

基于JavaSpringbootVue的养老院管理系统(源码数据库12000字论文)111 一、系统介绍 本系统前后端分离&#xff0c;本系统分为销售、人事、服务、餐饮、财务、超级管理员六种角色 系统主要功能如下&#xff1a; 首页统计&#xff1a;包括今日新增咨询、今日新增预定、今日新增…

AI资讯--Meta AI工具“指哪打哪“;OpenAI CEO事件梳理;

看点 Meta展示全新AI图像编辑工具&#xff1a;文本指令“指哪打哪”&#xff0c;主体背景都能换 &#x1f3a8;OpenAI CEO被董事会罢免36小时事件梳理 &#x1f552;OpenAI开掉了最能搞钱的创始人&#xff0c;GPT在他手里可能失控&#xff1f; &#x1f4b8;微软和兴盛资本向O…

树与二叉树堆:堆

堆的概念&#xff1a; 一般是把数组的数据在逻辑结构上看成一颗完全二叉树&#xff0c;如下图所示。 注意&#xff1a;别将C语言中的堆和数据结构的堆混为一谈&#xff0c;本文所讲的数据结构的堆是一种完全二叉树&#xff0c;而C语言中的堆其实是一种内存区域的划分 堆的分类…

Java语言基础第五天

精华笔记&#xff1a; 循环结构&#xff1a; for结构&#xff1a;应用率最高&#xff0c;与次数相关的循环 三种结构如何选择&#xff1a; 先看循环是否与次数相关&#xff1a; 若相关-----------------------------直接上for 若无关&#xff0c;再看要素1与要素3的代码是否相…

linux shell操作 - 04 进程间通信

文章目录 Signal 信号信号定义信号的生命周期信号分类linux进程通信案例 Signal 信号 信号定义 Linux信号是进程间通信的一种方式&#xff0c;通过向目标进程发送一个特定的信号&#xff0c;让其执行相应的处理操作&#xff1b; 向目标进程发送信号时&#xff0c;内核会将信号…

【发明专利】天洑软件再度收获六项国家发明专利授权

近日&#xff0c;南京天洑软件有限公司再度收获行业内六项国家发明专利授权&#xff0c;专利名称为&#xff1a;一种发电机绕组温度预警方法及装置&#xff08;专利号&#xff1a;ZL 2022 1 1525605.3&#xff09;&#xff0c;一种CSTR系统的控制方法及装置&#xff08;专利号&…

《C++PrimePlus》第8章 函数探幽

8.1 内联函数 使用内联函数 #include <iostream> using namespace std;inline double square(double x) { return x * x; }int main(){double a;a square(5.0);cout << "a " << a << endl;return 0; } 8.2 引用变量 将引用用作函数参数&…

java: 无效的目标发行版: 17 问题解决

今天在写完类点击运行后显示java: 无效的目标发行版: 17 网上查询了一番&#xff0c;发现有几个地方需要注意。 还有一个就是设置中&#xff0c;下面的就是我本次问题所在&#xff0c;不知道为什么&#xff0c;他自动添加了下面的东西 一个方法是把目标字节码版本改为正确的&a…

(C++)验证回文字符串

愿所有美好如期而遇 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台备战技术面试&#xff1f;力扣提供海量技术面试资源&#xff0c;帮助你高效提升编程技能&#xff0c;轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/valid-pali…

OpenAI 超 700 名员工联名逼宫董事会;ChatGPT 新功能“阅后即焚”丨 RTE 开发者日报 Vol.89

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」 &#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE &#xff08;Real Time Engagement&#xff09; 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思考的 文…

Go中各种newreader和newbuffer的使用

一、bytes.NewBuffer和bytes.NewReader func main() {var byteArr []bytebuf : bytes.NewBuffer(byteArr)buf.Write([]byte("今天不错"))fmt.Println(buf.String()) }package mainimport ("bytes""fmt" )func main() {data : []byte("路多…

数据资产到底如何入表?

2024年1月1日起&#xff0c;财政部《企业数据资源相关会计处理暂行规定》正式施行&#xff0c;距离现在只有一个多月的时间。 数据资源入表意味着企业可以将数据资源确认为企业资产负债表中“资产”一项。对于拥有丰富数据资源的企业来说&#xff0c;有望在财务报表中体现其真…

Spring Boot单元测试

目录 1.概述 2.基本使用 3.优势 4.常用属性 1.概述 所谓单元测试就是对功能最小粒度的测试&#xff0c;落实到JAVA中就是对单个方法的测试。对单个方法的测试用junit即可&#xff0c;关于junit作者另一位篇文章中有详细介绍&#xff0c;感兴趣的小伙伴可以去看看&#xff…

Sqlite安装配置及使用

一、下载SQLite Sqlite官网 我下载的是3370000版本:sqlite-dll-win64-x64-3370000.zip 和 sqlite-tools-win32-x86-3370000.zip 二、解压下载的两个压缩包 三、配置环境 四、检查是否安装配置成功 winR&#xff1a;输入cmd调出命令窗口&#xff0c;输入sqlite3后回车查看s…

软件运维面试题

文章目录 面试题你在设备安装时遇到了一个故障&#xff0c;你会怎么做&#xff1f;答案 作为一名实施工程师&#xff0c;你如何保证项目的交付质量&#xff1f;答案 作为一名实施工程师&#xff0c;你在处理技术问题时遇到过比较难解决的情况吗&#xff1f;如果有&#xff0c;可…

百度爬虫的工作原理解析

百度作为中国最大的搜索引擎&#xff0c;其工作原理备受关注。本文将深入探讨百度爬虫的工作原理&#xff0c;介绍其基本流程以及关键技术&#xff0c;帮助读者更好地理解搜索引擎背后的技术核心。 百度爬虫是百度搜索引擎的重要基石&#xff0c;它们被广泛用于收集互联网上的网…

怎样用css画一个圆?

要使用 CSS 画一个圆&#xff0c;可以使用 border-radius 属性为一个元素添加圆角&#xff0c;将 width 和 height 设置为相等的值&#xff0c;从而形成一个圆形。 以下是一个使用 CSS 画圆的简单示例&#xff1a; .circle {width: 100px;height: 100px;background-color: #3…

08-黑马点评项目发布笔记和查看笔记功能的实现

发布笔记 数据模型 tb_blog探店笔记表,包含笔记的标题、文字、图片等 tb_blog探店笔记表对应的实体类 增加用户图标和和用户姓名以及是否被点赞过了的字段,这些字段不属于Blog表只是为了实现在展示笔记的时候同时展示用户的信息 Data EqualsAndHashCode(callSuper false) …

使用sed命令进行文本处理示例

文章目录 前言查找并替换操作在文件中添加文本删除文件中的文本高亮显示文件中的文本 转载请标明出处&#xff1a; https://bigmaning.blog.csdn.net/article/details/134539923 本文出自:【BigManing的博客】 前言 当我们需要在Linux系统中处理文本文件时&#xff0c;经常需要…