我的编程空间,编程开发者的网络收藏夹
学习永远不晚

在Java中操作串口实现短信收发

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

在Java中操作串口实现短信收发

在Java中操作串口实现短信收发 (转载自:http://blog.csdn.net/sinboy/archive/2005/12/09/548158.aspx)[@more@]

采用串口操作进行短信收发,是比较常见的一种方式.比如,很多群发软件,用的就是这种方法.

配置comm.jar.

Comm.jar是Sub实现底层串口操作的API,调用了本地的DLL文件,因为Java本身不具备直接访问硬件设置的能力,都是通过调用本地方法来实现的.可以Java的官方网站下载.下载之后把其中Comm.jar包导入到工程的Classpath中,把另外两个非常重要的文件javax.comm.properties和win32com.dll考贝到你的工程目录下,即java.user下.

打开串口.

在打开串口前首先要加载Win32com.dll,因为我们没有把它放到JRE路径下,所以必须要自己显式的加载.

String driverName = "com.sun.comm.Win32Driver";
CommDriver driver = null;

try {
System.loadLibrary("win32com");
driver = (CommDriver) Class.forName(driverName).newInstance();
driver.initialize();
} catch (InstantiationException e1) {
logger.error("1:" + e1.getMessage());

} catch (IllegalAccessException e1) {
logger.error("2:" + e1.getMessage());

} catch (ClassNotFoundException e1) {
logger.error(e1.getMessage());
}

然后获取你指定的端口:

SerialPort sPort = null;
CommPortIdentifier portID;
String owner = new String("modemn");
int keeptime = 5000;
Enumeration portList;
portList = CommPortIdentifier.getPortIdentifiers();

// 如果有多个端口
while (portList.hasMoreElements()) {
portID = (CommPortIdentifier) portList.nextElement();
if (portID.getName().equals(com))
try {
sPort = (SerialPort) portID.open(owner, keeptime);
break;
}// 打开一个串口
catch (PortInUseException e) {
logger.fatal(e.getMessage());
System.exit(1);
}

}// while

成功打开端口之后,设置端口的相关参数,如波特率、数据位、奇偶校验位等.这个跟具体的设备有关,不过一般情况下波特率为9600,数据位为8,停止位为1,奇偶为0,流量控制为Off:

if (sPort != null) {
logger.debug("serial name is :" + sPort.getName());
try {
// 设置串口的参数
sPort.setSerialPortParams(9600,// 波特率
SerialPort.DATABITS_8,// 数据位数
SerialPort.STOPBITS_1, // 停止位
SerialPort.PARITY_NONE);// 奇偶位
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}

对端口进行初始化

对进行数据接收或发送之前,还要先进行一些参数的设置。重要的有:

AT+cmgf=0(设置Modem收发采用Pdu方式,1为Text方式。有些Modem可能正好相反,具体参考Modem的At指令说明)

At+cnmi=2,2,0,0,0(设置Modem自动接收,AT指令说明书给的定义是新的短消息指示说明,就是说说有了新的短消息,怎么给你提示。这个设置是有消息将自动显示,无需进行读卡操作。看到有很网上的例子都是1,1,这样还要通过读卡操作才能得到短消息,十分不方便,还降低了SIM卡的使用寿命)

At+csmp=17,167,0,240(设置短消息文本模式参数。其中17是指SMS-SUBMIT的十进制整数表达形式,即提交;167指是有效期的整数表达形式;0指的是协议标识的十进制整数表示形式。前三个参数都该命令的默认值。最后一240指是编码方案,在Text方式下发送英文和Pdu模式下一般设置成240.如果要在Text模式下发送中文,有多Modem要设成8)

对端口所作的上述初始化工作,可以在超终终端里直接设置。但最好是把它写在程序里,在程序启动之后就进行此工作,避免手工操作的麻烦。

对Modem进行初始化,就必须把上述命令输出到Modem的端口上,还要看它的反回值是不是OK。要想得到返回值,就要对COM端口进行侦听了。所以初始化的工作有三步:

第一,侦听端口
sPort.addEventListener(this);
sPort.notifyOnDataAvailable(true);

第二,建立输入输出流,把初始化命令输出到Modem的COM端口

// 用配置参数初始化MODEM
msg = conf.initParam();
if (msg != null) {
if (conf.modemMode() != null && conf.modemMode().equals("0"))
if (isPduMode)
msg = "at+cmgf=0;" + msg;
else
msg = "at+cmgf=1;" + msg;
sendMsg(msg.getBytes(), sPort);
sendOKFlag = true;
}

// 把短消息通过数据猫发送出去
private void sendMsg(byte[] msg, SerialPort sPort) {

DataOutputStream pw;
if (msg != null && sPort != null)
try {

pw = new DataOutputStream(sPort.getOutputStream());
pw.write(msg);

pw.flush();
pw.close();
logger.debug("msg has been send from Modemn:");

} catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}

// 处理侦听到的串口事件
public synchronized void serialEvent(SerialPortEvent ev) {

DataInputStream in;
int c = 0;
StringBuffer sb = null;
// 如果有串口事件发生
if (ev.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

try {
in = new DataInputStream(sPort.getInputStream());
sb = new StringBuffer();
while ((c = in.read()) != -1) {
sb.append((char) c);

System.out.println(sb);
if (handleRecData(sb)) {
logger.debug("从Modem接收到的数据" + sb);
sb = new StringBuffer();

}

}

}// try
catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}
}

serialEvent事件就是刚才添加侦听之后要工作的部分。如果写过界面程序的人,对这个会比较熟悉。一但Modem回复数据,此事件就会触发。我们在发送完初始化命令之后,就从此事件中接收数据,看能不能收到OK。如果收到,就初始化成功。

发送数据

成功进行初始化之后,就可以进行正常的数据收发了。我们在此使用PDU的方式做说明,Text方式一是很多Modem或手机都不支持,二是也比较简单,在最后再提。

首先我们可以把要发送的数据放到发送缓冲列表中,对数据一条一条的发送。因为Modem不支持异步(我自己的经验),我们只能发完一条之后,再发第二条。发送的原理同发送初始化命令一样,但是要注意的一点是发送内容首先要进行PDU编码,发送的格式为:

AT+CMGS=PDU DATA

其中Length为经过编码之后的PDU data的长度减去18再除以2得到,至于为什么这样算,可以参考AT命令手册。有些Modem支持直接把上面的做为一个完整的数据包发送过去(比如:FALCOM系列的),有的就非常的笨(比如:西门子 Tc35i),它要求必须先发前面的At命令,等接收到回应“>”符号之后,再发送PDU DATA,十分麻烦,一点去不为咱们开发人员着想:(,我就是在这吃的亏。按以前的习惯做,结果怎么都发不出去。

步骤是这样的:

首先取出经过PDU编码过的数据,计算它的长度,生成At+cmgs=

然后发送出去

接着侦听COM端口,等待接收">"字符

接收“>”之后,接着发送剩余的部分

然后再侦听,接收回应,看是否发送成功。如果发送成功的话,一般会接到这样的数据:

数据本身:PDU DATA

发送序号:+CMGS:22

成功标志:OK

有时候也能接收OK,但是收不到+CMGS:22这样的东西,还是没有发送成功,这两个都必须有,才真正发送成功。

接收数据

接收数据其实也是在serialEvent里面处理。如果象上面的设置那样,设成自动接收,那么一但有短消息来了之后,自动触发事件,完整的数据格式大概是这样的:

+CMT:

PDU Data

"nr"

最后是以回车换行结束

我们收到完整的数据之后,把PDU data取出来,再按PDU编码进行解码就行。

调试

首先最好能用超级终端,因为它使用起来很方便,直接输命令,直接看结果。

还有一些非常好的串口监测调试软件,能直接显示出来你向串口都发了什么数据,从串口接收到了什么数据,发送接收的对错,一看就清楚了。我在调TC35i的时候就折腾了好几天,就是不知道是什么原因,最后有网友建议我用串口监测软件,结果我没有半个小时就把问题搞定了,不是它,我都要哭了。强烈推荐一款是AcessPort,中文,免费,非常好用。

关于Text方式

格式如下:

AT+CMGS="+8613912345678"Text content

如果是英文数字的话,直接发送就行了,接收到的也是Ascii码,无需编码

如果是中文的话,要先进行Unicode编码,接收也一样,收到之后要进行Unicode转gb的转换

另外先把配置设置好

参考源代码:

package com.gftech.dcs.commu;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.TooManyListenersException;

import javax.comm.CommDriver;
import javax.comm.CommPortIdentifier;
import javax.comm.PortInUseException;
import javax.comm.SerialPort;
import javax.comm.SerialPortEvent;
import javax.comm.SerialPortEventListener;
import javax.comm.UnsupportedCommOperationException;

import org.apache.log4j.Logger;
import org.apache.log4j.xml.DOMConfigurator;

import com.gftech.common.PduPack;
import com.gftech.smp.DeliverPack;
import com.gftech.smp.SubmitPack;


public class ModemAdapter extends Thread implements SerialPortEventListener {
private static ModemAdapter modem;

// 发送是否已成功完成
private boolean sendOKFlag;

private int errCount;

// 发送模式是否是PDU方式
private boolean isPduMode;

private String smContent;

private ArrayList sendBuffer;

// 要打开使用的串口
private SerialPort sPort;

static Logger logger = Logger.getLogger(ModemAdapter.class);

private ModemAdapter() {
DOMConfigurator.configure(MyFinal.LOG4J_CONF_FILE);

isPduMode = false;
errCount = 0;

logger.debug("Add a test data");
sendBuffer = new ArrayList();
SubmitPack msg = new SubmitPack();
ArrayList destList = new ArrayList();
destList.add("136××××××××");
msg.setSm("你好,张新波");
msg.setDestAddr(destList);
add(msg);

start();

}

public static ModemAdapter getInstance() {
if (modem == null)
modem = new ModemAdapter();
return modem;
}

// 得到计算机的串口
private SerialPort getSerialPort(String com) {
SerialPort sPort = null;
CommPortIdentifier portID;
String owner = new String("modemn");
int keeptime = 5000;
Enumeration portList;
portList = CommPortIdentifier.getPortIdentifiers();

String driverName = "com.sun.comm.Win32Driver";
CommDriver driver = null;

try {
System.loadLibrary("win32com");
logger.debug("Win32Com Library Loaded");

driver = (CommDriver) Class.forName(driverName).newInstance();
driver.initialize();
logger.debug("Win32Driver Initialized");
} catch (InstantiationException e1) {
logger.error("1:" + e1.getMessage());
e1.printStackTrace();
} catch (IllegalAccessException e1) {
logger.error("2:" + e1.getMessage());
e1.printStackTrace();
} catch (ClassNotFoundException e1) {
logger.error(e1.getMessage());
e1.printStackTrace();
}
// 如果有多个端口
while (portList.hasMoreElements()) {
portID = (CommPortIdentifier) portList.nextElement();
if (portID.getName().equals(com))
try {
sPort = (SerialPort) portID.open(owner, keeptime);
break;
}// 打开一个串口
catch (PortInUseException e) {
logger.fatal(e.getMessage());
System.exit(1);
}

}// while

if (sPort != null) {
logger.debug("serial name is :" + sPort.getName());
try {
// 设置串口的参数
sPort.setSerialPortParams(9600,// 波特率
SerialPort.DATABITS_8,// 数据位数
SerialPort.STOPBITS_1, // 停止位
SerialPort.PARITY_NONE);// 奇偶位
} catch (UnsupportedCommOperationException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}

return sPort;
}

private boolean init() {
boolean result = false;
Config conf = new Config();
String comName = conf.comName();
sPort = getSerialPort(comName);
String msg = null;

if (sPort != null) {
listenSerialPort(sPort);
// 用配置参数初始化MODEM
msg = conf.initParam();
if (msg != null) {
if (conf.modemMode() != null && conf.modemMode().equals("0"))
isPduMode = true;

if (isPduMode)
msg = "at+cmgf=0;" + msg;
else
msg = "at+cmgf=1;" + msg;
sendMsg(msg, sPort);
sendOKFlag = true;
}
}

for (int i = 0; i < 100; i++) {
try {
Thread.sleep(1000);

if (sendOKFlag) {
logger.debug("初始化MODEM成功!");
return true;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}

return result;
}

// 把短消息通过数据猫发送出去
private void sendMsg(String msg, SerialPort sPort) {

PrintWriter pw;
if (msg != null && sPort != null)
try {

pw = new PrintWriter(sPort.getOutputStream());
pw.println(msg);

pw.flush();
pw.close();
logger.debug("msg has been send from Modemn:");
logger.debug(msg);
} catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}

// 把短消息通过数据猫发送出去
private void sendMsg(byte[] msg, SerialPort sPort) {

DataOutputStream pw;
if (msg != null && sPort != null)
try {

pw = new DataOutputStream(sPort.getOutputStream());
pw.write(msg);

pw.flush();
pw.close();
logger.debug("msg has been send from Modemn:");

} catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}

private void listenSerialPort(SerialPort sPort) {

if (sPort != null)
try {

sPort.addEventListener(this);
sPort.notifyOnDataAvailable(true);
} catch (TooManyListenersException e) {
logger.error(e.getMessage());
e.printStackTrace();
}

}

public void run() {
int waitCount = 0;
String cmd1 = null;

SubmitPack msg;
String dest;
String content;

if (init()) {

while (true) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
msg = getOneMsg();
if (msg != null) {
ArrayList destList = msg.getDestAddr();
for (int i = 0; destList != null && i < destList.size(); i++) {
dest = (String) destList.get(i);
content = msg.getSm();

if (content != null) {
while (true) {
if (sendOKFlag == true) {
if (isPduMode) {
Config conf = new Config();
PduPack pack = new PduPack();
pack.setAddr(dest);
pack.setMsgContent(content);
pack.setSmsc(conf.smsc());
String coded = pack.getCodedResult();
if (coded != null
&& coded.length() > 18)
cmd1 = "AT+CMGS="
+ (coded.length() - 18) / 2
+ "r";
smContent = coded
+ (char) Integer.parseInt("1A",
16);

// cmd1+=smContent;
sendMsg(cmd1.getBytes(), sPort);
cmd1 = null;

} else
cmd1 = "AT+CMGS="+86"
+ dest
+ ""r"
+ content
+ (char) Integer.parseInt("1a",
16) + "z";
if (cmd1 != null) {
logger.debug("Cmd:" + cmd1);
sendMsg(cmd1, sPort);
sendOKFlag = false;
logger.debug("isSendOK=false");
}
break;
} else
try {
sleep(100);
if (waitCount > 300) {
sendOKFlag = true;
waitCount = 0;
} else
waitCount++;
} catch (InterruptedException e) {
}
}
}
}
}

}// while
} else {
logger.fatal("无法成功初始化MODEM,请检查设备");
System.exit(0);
}

}

// 处理侦听到的串口事件
public synchronized void serialEvent(SerialPortEvent ev) {

DataInputStream in;
int c = 0;
StringBuffer sb = null;
// 如果有串口事件发生
if (ev.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

try {
in = new DataInputStream(sPort.getInputStream());
sb = new StringBuffer();
while ((c = in.read()) != -1) {
sb.append((char) c);

System.out.println(sb);
if (handleRecData(sb)) {
logger.debug("从Modem接收到的数据" + sb);
sb = new StringBuffer();

}

}

}// try
catch (IOException e) {
logger.error(e.getMessage());
e.printStackTrace();
}
}
}


private boolean isRecOK(String data) {
final String OK_FLAG = "OK";
int index1 = 0;

if (data != null) {
index1 = data.indexOf(OK_FLAG);

if (index1 >= 0 && index1 + 4 <= data.length()) {
String t = data.substring(index1 + 2);
byte[] b = t.getBytes();
if (b.length >= 2) {
if (b[0] == 0x0D && b[1] == 0x0A)
return true;
}
}
}

return false;
}


private boolean isSendOK(String data) {
final String FLAG = "+CMGS:";
int index = -1;
int index2 = -1;

if (data != null) {
index = data.indexOf(FLAG);
if (index > 0) {
index += 6;
if (index < data.length()) {
String temp = data.substring(index);
index = 0;
byte[] b = temp.getBytes();
for (int i = 0; i < b.length; i++) {
if (b[i] == 0x0D) {
index2 = i;
break;
}
}

if (index2 < temp.length() && index2 > index + 1) {
String t1 = temp.substring(index + 1, index2);

try {
int seqid = Integer.parseInt(t1);
logger.debug("seqID:" + seqid);

if (index2 + 8 == temp.length()) {
// 两个回车换行符
if (b[index2] == 0x0D && b[++index2] == 0x0A
&& b[++index2] == 0x0D
&& b[++index2] == 0x0A) {
if (b[++index2] == 0x4F
&& b[++index2] == 0x4B) {// OK
if (b[++index2] == 0x0D
&& b[++index2] == 0x0A) {// 一个回车换行
return true;
}
}
}
}
} catch (NumberFormatException e) {
e.printStackTrace();
return false;
}
}
}
}
}

return false;
}


private boolean isRecError(String data) {

final String FLAG = "ERROR";

int index1 = 0;

if (data != null) {
index1 = data.indexOf(FLAG);

if (index1 >= 0 && index1 + 7 <= data.length()) {
String t = data.substring(index1 + 5);
byte[] b = t.getBytes();
if (b.length >= 2) {
if (b[0] == 0x0D && b[1] == 0x0A)
return true;
}
}
}

return false;
}


private boolean isRecData(String data) {
final String BEGIN_FLAG = "+CMT:";
int index0 = -1;
int index1 = -1;
int index2 = -1;

if (data != null) {
index0 = data.indexOf(BEGIN_FLAG);
if (index0 >= 0 && index0 < data.length()) {
// data=data.substring(index0);
// index1 = data.indexOf("rn");
// if (index1 > index0 && index1 + 2 < data.length()) {
// String str=data.substring(index1+2);
// index2=str.indexOf("rn");
// if(index2>0 && index2 //
// return true;
// }
// }

return true;

}
}
return false;
}

private boolean handleRecData(StringBuffer sb) {
String data = null;

if (sb != null) {
data = sb.toString();
if (isRecOK(data)) {
sendOKFlag = true;
return true;
} else if (isRecError(data)) {
errCount++;
if (errCount > 3) {
sendOKFlag = true;
errCount = 0;
}

return true;
} else if (sb.indexOf(">") != -1 && smContent != null) {
sendMsg(smContent.getBytes(), sPort);
smContent = null;
}

else {
int index0 = data.lastIndexOf("+CMT:");

if (index0 >= 0 && index0 < data.length()) {
data = data.substring(index0);

int index1 = data.indexOf("rn");
if (index1 != -1 && index1 + 2 < data.length()) {
data = data.substring(index1 + 2);

int index2 = data.indexOf("rn");
if (index2 > 1 && index2 < data.length()) {
data = data.substring(0, index2);
if (data != null && data.length() > 0) {
PduPack pack = new PduPack(data);
String class="lazy" data-srcAddr = pack.getAddr();
String content = pack.getMsgContent();
String destAddr = "012345";

if (class="lazy" data-srcAddr != null && content != null) {
logger.debug("class="lazy" data-srcAddr:"+class="lazy" data-srcAddr);
logger.debug("content:"+content);

Config conf = new Config();
destAddr = conf.cmppSSN();
DeliverPack deliver = new DeliverPack(
class="lazy" data-srcAddr, destAddr, content);
ServerAdapter server = ServerAdapter
.getInstance();
server.addDeliverPack(deliver);

return true;
}
}
}
}
}

}

}
return false;
}

private SubmitPack getOneMsg() {
SubmitPack result = null;

if (sendBuffer != null && sendBuffer.size() > 0)
result = (SubmitPack) sendBuffer.remove(0);
return result;
}

public void add(SubmitPack msg) {
sendBuffer.add(msg);
}

}

结束语

在如下环境调试通过:

Eclipse3.1,Jdk1.5,西门子 Tc35i Modem

关于PDU编码的详细说明,请参考bhw98的专栏

关于PDU编码实现的Java源代码,我会在另外的文章中介绍。

参考资料

*《AT Command set-Siemens Celluler Engines》

*bhw98的专栏 http://blog.csdn.net/bhw98/archive/2003/03/27/19666.aspx

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

在Java中操作串口实现短信收发

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

在Java中操作串口实现短信收发

在Java中操作串口实现短信收发 (转载自:http://blog.csdn.net/sinboy/archive/2005/12/09/548158.aspx)[@more@]采用串口操作进行短信收发,是比较常见的一种方式.比如,很多群发
2023-06-03

Java中怎么实现SMS短信通发送手机验证码

本篇文章给大家分享的是有关Java中怎么实现SMS短信通发送手机验证码,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。设置短信签名注意不要乱写别的公司等,会被视为诈骗信息设置短信
2023-06-20

如何在java中使用HttpURLConnection实现发送文件与字符串信息

这篇文章给大家介绍如何在java中使用HttpURLConnection实现发送文件与字符串信息,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。java HttpURLConnection 发送文件和字符串信息以文件的形
2023-05-31

如何在PHP中实现用户注册时发送短信验证码

随着移动互联网的普及,手机号码已经成为用户注册和登录的重要凭证之一。为了保证用户账号的安全性,很多网站和应用都会在用户注册时发送短信验证码进行验证。本文将介绍如何在PHP中实现用户注册时发送短信验证码的功能,并附上具体的代码示例。一、创建短
2023-10-21

如何在Android开发中利用Mob实现一个短信验证码

如何在Android开发中利用Mob实现一个短信验证码?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。前期准备(1)Mob网站上下载SMS对应的SDK,解压出来为图1,打开图1,
2023-05-31

【JAVA】有关时间的操作在编程中如何实现?

个人主页:【😊个人主页】 系列专栏:【❤️初识JAVA】 文章目录 前言Date 类Date 类方法Data的缺陷实例获取当前日期时间日期比较java中设置date数据的显示格式 前言 在许多应
2023-08-16

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录