SQLite 使用

转载 2016年08月31日 11:49:57
 SQLite是一个开源的跨平台的轻型数据库,WINCE本身也有一个自带的数据库SQLCE ,但占用的资源会比较大。最近项目中考虑用到 SQLite,因此特别研究了一下,下面介绍一下具体的移植方法。

一、下载SQLite源码

       去SQLite官网http://www.sqlite.org/download.htm下载最新的source code。我下载的是sqlite-amalgamation-3071401.zip。解压后会得得到四个文件(shell.c、sqlite3.c、sqlite3.h、sqlite3ext.h)。

二、编译生成WINCE下DLL

      1. 在VS2005下新建一个Win32智能设备项目,选择相应的SDK,并选择应用程序类型为DLL,新建空项目

      2. 将sqlite-amalgamation-3071401.zip解压后的sqlite3.c、sqlite3.h文件拷贝到工程目录下并添加至工程目录。如下图所示。

            

       3.在预处理中增加SQLITE_ENABLE_RTREE和SQLITE_ENABLE_COLUMN_METADATA宏定义。如图所示:

           

        4.编译项目,会提示“error LNK2019: 无法解析的外部符号 localtime_s,该符号在函数 osLocaltime 中被引用”错误,此错误的解决方法是将localtime_s替换成_localtime64_s即可。

        5.编译成功后会发现目录下会生成SQLite.dll文件,但会发现没有lib文件,这样在使用这个DLL时是没法编译的,所以还需要导出.lib文件,解决方法是在SQLite官网上下载sqlite-dll-win32-x86-XXXXXX.zip文件,解压后将目录下的sqlite3.def文件拷贝到DLL工程目录,在项目属性--链接器--输入--模块定义文件中添加sqlite3.def,如下图所示,然后编译即可。

           

这样SQLite的编译工作就大功告成了。

三、WINCE 下SQLite测试

        新建一个SQLite测试程序,测试代码如下:      

#include <windows.h>

// sqlite3的回调函数
int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name )

int main(int argc,char* argv[])
{
	sqlite3 * db = NULL; //声明sqlite关键结构指针
	int result;
	// 打开或创建数据库
	result = sqlite3_open("NAND2\\sqlite.db", &db );
	if( result != SQLITE_OK )
	{
		//数据库打开失败
		return -1;
	}
	char * errmsg = NULL;
	// 数据库操作代码
#if 1
	// 创建一个测试表,表名叫 MyTable,有2个字段: ID 和 name。其中ID是一个自动增加的类型,以后insert时可以不去指定这个字段,它会自己从0开始增加
	result = sqlite3_exec( db, "create table MyTable( ID integer primary key autoincrement, name nvarchar(32) )", NULL, NULL, &errmsg );
	if(result != SQLITE_OK )
	{
		printf("创建表失败,错误码:%d,错误原因:%s\n", result, errmsg );
	}
	// 插入记录
	result = sqlite3_exec( db, "insert into MyTable( name ) values ( '张三' )", 0, 0, &errmsg );
	if(result != SQLITE_OK )
	{
		printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg );
	}
	// 插入记录
	result = sqlite3_exec( db, "insert into MyTable( name ) values ( '李四' )", 0, 0, &errmsg );
	if(result != SQLITE_OK )
	{
		printf("插入记录失败,错误码:%d,错误原因:%s\n", result, errmsg );
	}
#endif
	// 开始查询数据库
	result = sqlite3_exec( db, "select * from MyTable", SQLiteQueryResultCallBack, NULL, &errmsg );
	// 关闭数据库
	sqlite3_close( db );

	return 0;
}

// sqlite3的回调函数
int SQLiteQueryResultCallBack( void * para, int n_column, char ** column_value, char ** column_name )
{
	printf( "******************************\n" );
	printf("记录包含 %d 个字段\n", n_column );
	for(int i = 0 ; i < n_column; i ++ )
	{
		printf( "字段名:%s 字段值:%s\n", column_name[i], column_value[i] );
	}
	printf( "******************************\n" );
	return 0;
}



四、SQLite可视化管理工具

        SQLite本身没有可视化管理工具,只提供了一个命令行的管理工具SQLite.exe。有一个第三方的可视化管理工具Sqlite Expert,用着还可以,下载地址: http://www.sqliteexpert.com/download.html



C++使用SQLite

头文件(SQLite.h)

/********************************************************************
filename: 	SQLite.h
created:	2012-11-05
author:		firehood

purpose:	SQLite数据库操作类
*********************************************************************/
#pragma once
#include <windows.h>
#include "..\SQLite\sqlite3.h" 
#pragma comment(lib,"SQLite.lib") 

typedef BOOL (WINAPI *QueryCallback) (void *para, int n_column, char **column_value, char **column_name);

typedef enum _SQLITE_DATATYPE
{
	SQLITE_DATATYPE_INTEGER = SQLITE_INTEGER,
	SQLITE_DATATYPE_FLOAT  = SQLITE_FLOAT,
	SQLITE_DATATYPE_TEXT  = SQLITE_TEXT,
	SQLITE_DATATYPE_BLOB = SQLITE_BLOB,
	SQLITE_DATATYPE_NULL= SQLITE_NULL,
}SQLITE_DATATYPE;

class SQLite;

class SQLiteDataReader
{
public:
	SQLiteDataReader(sqlite3_stmt *pStmt);
	~SQLiteDataReader();
public:
	// 读取一行数据
	BOOL Read();
	// 关闭Reader,读取结束后调用
	void Close();
	// 总的列数
    int ColumnCount(void);
	// 获取某列的名称 
	LPCTSTR GetName(int nCol);
	// 获取某列的数据类型
	SQLITE_DATATYPE GetDataType(int nCol);
	// 获取某列的值(字符串)
	LPCTSTR GetStringValue(int nCol);
	// 获取某列的值(整形)
	int GetIntValue(int nCol);
	// 获取某列的值(长整形)
	long GetInt64Value(int nCol);
	// 获取某列的值(浮点形)
	double GetFloatValue(int nCol);
	// 获取某列的值(二进制数据)
	const BYTE* GetBlobValue(int nCol, int &nLen);
private:
	sqlite3_stmt *m_pStmt;
};

class SQLiteCommand
{
public:
	SQLiteCommand(SQLite* pSqlite);
	SQLiteCommand(SQLite* pSqlite,LPCTSTR lpSql);
	~SQLiteCommand();
public:
	// 设置命令
	BOOL SetCommandText(LPCTSTR lpSql);
	// 绑定参数(index为要绑定参数的序号,从1开始)
	BOOL BindParam(int index, LPCTSTR szValue);
	BOOL BindParam(int index, const int nValue);
	BOOL BindParam(int index, const double dValue);
	BOOL BindParam(int index, const unsigned char* blobValue, int nLen);
	// 执行命令
	BOOL Excute();
	// 清除命令(命令不再使用时需调用该接口清除)
	void Clear();
private:
	SQLite *m_pSqlite;
	sqlite3_stmt *m_pStmt;
};

class SQLite
{
public:
	SQLite(void);
	~SQLite(void);
public:
	// 打开数据库
    BOOL Open(LPCTSTR lpDbFlie);
	// 关闭数据库
	void Close();

    // 执行非查询操作(更新或删除)
	BOOL ExcuteNonQuery(LPCTSTR lpSql);
	BOOL ExcuteNonQuery(SQLiteCommand* pCmd);

    // 查询
	SQLiteDataReader ExcuteQuery(LPCTSTR lpSql);
	// 查询(回调方式)
	BOOL ExcuteQuery(LPCTSTR lpSql,QueryCallback pCallBack);

	// 开始事务
	BOOL BeginTransaction();
	// 提交事务
	BOOL CommitTransaction();
	// 回滚事务
	BOOL RollbackTransaction();

	// 获取上一条错误信息
	LPCTSTR GetLastErrorMsg();
public:
	friend class SQLiteCommand;
private:
	sqlite3 *m_db;
};

</span>

源文件(SQLite.cpp)

/********************************************************************
filename: 	SQLite.cpp
created:	2012-11-05
author:		firehood

purpose:	SQLite数据库操作类
*********************************************************************/
#include "SQLite.h"

const char* WcharToUtf8(const wchar_t *pwStr)
{
	if (pwStr == NULL)
	{
		return NULL;
	}

	int len = WideCharToMultiByte(CP_UTF8, 0, pwStr, -1, NULL, 0, NULL, NULL);
	if (len <= 0)
	{
		return NULL;
	}
	char *pStr = new char[len];
	WideCharToMultiByte(CP_UTF8, 0, pwStr, -1, pStr, len, NULL, NULL);
	return pStr;
}

const wchar_t* Utf8ToWchar(const char *pStr)
{
	if (pStr == NULL)
	{
		return NULL;
	}

	int len = MultiByteToWideChar(CP_UTF8, 0, pStr, -1, NULL, 0);
	if (len <= 0)
	{
		return NULL;
	}
	wchar_t *pwStr = new wchar_t[len];
	MultiByteToWideChar(CP_UTF8, 0, pStr, -1, pwStr, len);
	return pwStr;
}

SQLite::SQLite(void):
m_db(NULL)
{
}

SQLite::~SQLite(void)
{
	Close();
}

BOOL SQLite::Open(LPCTSTR lpDbFlie)
{
	if(lpDbFlie == NULL)
	{
		return FALSE;
	}
#ifdef  UNICODE 
	if(sqlite3_open16(lpDbFlie,&m_db) != SQLITE_OK)
#else
    if(sqlite3_open(lpDbFlie,&m_db) != SQLITE_OK)
#endif
	{
		return FALSE;
	}
	return TRUE;
}

void SQLite::Close()
{
	if(m_db)
	{
		sqlite3_close(m_db);
		m_db = NULL;
	}
}

BOOL SQLite::ExcuteNonQuery(LPCTSTR lpSql)
{
	if(lpSql == NULL)
	{
		return FALSE;
	}
    sqlite3_stmt* stmt;  
#ifdef  UNICODE 
	if(sqlite3_prepare16_v2(m_db, lpSql, -1, &stmt, NULL) != SQLITE_OK)
#else
    if(sqlite3_prepare_v2(m_db, lpSql, -1, &stmt, NULL) != SQLITE_OK)
#endif
	{ 
		return FALSE;  
	}  
	sqlite3_step(stmt);
	
	return (sqlite3_finalize(stmt) == SQLITE_OK) ? TRUE : FALSE ;
}

BOOL SQLite::ExcuteNonQuery(SQLiteCommand* pCmd)
{
	if(pCmd == NULL)
	{
		return FALSE;
	}
	return pCmd->Excute();
}

// 查询(回调方式)
BOOL SQLite::ExcuteQuery(LPCTSTR lpSql,QueryCallback pCallBack)
{
	if(lpSql == NULL || pCallBack == NULL)
	{
		return FALSE;
	}
	char *errmsg = NULL;
#ifdef  UNICODE 
	const char *szSql = WcharToUtf8(lpSql);
	if(sqlite3_exec(m_db, szSql, pCallBack, NULL, &errmsg) != SQLITE_OK)
	{
		delete[] szSql;
		return FALSE;
	} 
    delete[] szSql;
#else
	if(sqlite3_exec(m_db, lpSql, pCallBack, NULL, &errmsg) != SQLITE_OK)
	{
		return FALSE;
	} 
#endif
	return TRUE;
}

// 查询
SQLiteDataReader SQLite::ExcuteQuery(LPCTSTR lpSql)
{
	if(lpSql == NULL)
	{
		return FALSE;
	}
    sqlite3_stmt* stmt; 
#ifdef  UNICODE 
	if(sqlite3_prepare16_v2(m_db, lpSql, -1, &stmt, NULL) != SQLITE_OK)
#else
	if(sqlite3_prepare_v2(m_db, lpSql, -1, &stmt, NULL) != SQLITE_OK)
#endif
	{ 
		return FALSE;  
	}  
	return SQLiteDataReader(stmt);
}

// 开始事务
BOOL SQLite::BeginTransaction()
{
	char * errmsg = NULL;
    if(sqlite3_exec(m_db,"BEGIN TRANSACTION;",NULL,NULL,&errmsg) != SQLITE_OK)
	{
		return FALSE;
	} 
	return TRUE;
}

// 提交事务
BOOL SQLite::CommitTransaction()
{
	char * errmsg = NULL;
	if(sqlite3_exec(m_db,"COMMIT TRANSACTION;;",NULL,NULL,&errmsg) != SQLITE_OK)
	{
		return FALSE;
	} 
	return TRUE;
}

// 回滚事务
BOOL SQLite::RollbackTransaction()
{
	char * errmsg = NULL;
	if(sqlite3_exec(m_db,"ROLLBACK  TRANSACTION;",NULL,NULL,&errmsg) != SQLITE_OK)
	{
		return FALSE;
	} 
	return TRUE;
}

// 获取上一条错误信息
LPCTSTR SQLite::GetLastErrorMsg()
{
#ifdef UNICODE 
	return (LPCTSTR)sqlite3_errmsg16(m_db);
#else
    return sqlite3_errmsg(m_db);
#endif
}



SQLiteDataReader::SQLiteDataReader(sqlite3_stmt *pStmt):
m_pStmt(pStmt)
{

}

SQLiteDataReader::~SQLiteDataReader()
{
	Close();
}

// 读取一行数据
BOOL SQLiteDataReader::Read()
{
	if(m_pStmt == NULL)
	{
		return FALSE;
	}
	if(sqlite3_step(m_pStmt) != SQLITE_ROW)
	{
		return FALSE;
	}
	return TRUE;
}

// 关闭Reader,读取结束后调用
void SQLiteDataReader::Close()
{
	if(m_pStmt)
	{
		sqlite3_finalize(m_pStmt);
		m_pStmt = NULL;
	}
}

// 总的列数
int SQLiteDataReader::ColumnCount(void)
{
	return sqlite3_column_count(m_pStmt);
}

// 获取某列的名称 
LPCTSTR SQLiteDataReader::GetName(int nCol)
{
#ifdef  UNICODE 
	return (LPCTSTR)sqlite3_column_name16(m_pStmt, nCol);
#else
	return (LPCTSTR)sqlite3_column_name(m_pStmt, nCol);
#endif
}

// 获取某列的数据类型
SQLITE_DATATYPE SQLiteDataReader::GetDataType(int nCol)
{
	return (SQLITE_DATATYPE)sqlite3_column_type(m_pStmt, nCol);
}

// 获取某列的值(字符串)
LPCTSTR SQLiteDataReader::GetStringValue(int nCol)
{
#ifdef  UNICODE 
	return (LPCTSTR)sqlite3_column_text16(m_pStmt, nCol);
#else
    return (LPCTSTR)sqlite3_column_text(m_pStmt, nCol);
#endif
}

// 获取某列的值(整形)
int SQLiteDataReader::GetIntValue(int nCol)
{
	return sqlite3_column_int(m_pStmt, nCol);
}

// 获取某列的值(长整形)
long SQLiteDataReader::GetInt64Value(int nCol)
{
	return (long)sqlite3_column_int64(m_pStmt, nCol);
}

// 获取某列的值(浮点形)
double SQLiteDataReader::GetFloatValue(int nCol)
{
	return sqlite3_column_double(m_pStmt, nCol);
}

// 获取某列的值(二进制数据)
const BYTE* SQLiteDataReader::GetBlobValue(int nCol, int &nLen)
{
	nLen = sqlite3_column_bytes(m_pStmt, nCol);
	return (const BYTE*)sqlite3_column_blob(m_pStmt, nCol);
}

SQLiteCommand::SQLiteCommand(SQLite* pSqlite):
m_pSqlite(pSqlite),
m_pStmt(NULL)
{
}

SQLiteCommand::SQLiteCommand(SQLite* pSqlite,LPCTSTR lpSql):
m_pSqlite(pSqlite),
m_pStmt(NULL)
{
	SetCommandText(lpSql);
}

SQLiteCommand::~SQLiteCommand()
{

}

BOOL SQLiteCommand::SetCommandText(LPCTSTR lpSql)
{
#ifdef  UNICODE 
	if(sqlite3_prepare16_v2(m_pSqlite->m_db, lpSql, -1, &m_pStmt, NULL) != SQLITE_OK)
#else
	if(sqlite3_prepare_v2(m_pSqlite->m_db, lpSql, -1, &m_pStmt, NULL) != SQLITE_OK)
#endif
	{ 
		return FALSE;  
	}  
	return TRUE;
}

BOOL SQLiteCommand::BindParam(int index, LPCTSTR szValue)
{
#ifdef  UNICODE 
	if(sqlite3_bind_text16(m_pStmt, index, szValue, -1, SQLITE_TRANSIENT) != SQLITE_OK)
#else
    if(sqlite3_bind_text(m_pStmt, index, szValue,-1, SQLITE_TRANSIENT) != SQLITE_OK)
#endif
	{
		return FALSE;
	}
	return TRUE;
}

BOOL SQLiteCommand::BindParam(int index, const int nValue)
{
	if(sqlite3_bind_int(m_pStmt, index, nValue) != SQLITE_OK)
	{
		return FALSE;
	}
	return TRUE;
}

BOOL SQLiteCommand::BindParam(int index, const double dValue)
{
	if(sqlite3_bind_double(m_pStmt, index, dValue) != SQLITE_OK)
	{
		return FALSE;
	}
	return TRUE;
}

BOOL SQLiteCommand::BindParam(int index, const unsigned char* blobBuf, int nLen)
{
	if(sqlite3_bind_blob(m_pStmt, index, blobBuf,nLen,NULL) != SQLITE_OK)
	{
		return FALSE;
	}
	return TRUE;
}

BOOL SQLiteCommand::Excute()
{
	sqlite3_step(m_pStmt);

	return (sqlite3_reset(m_pStmt) == SQLITE_OK) ? TRUE : FALSE ;
}

void SQLiteCommand::Clear()
{
	if(m_pStmt)
	{
		sqlite3_finalize(m_pStmt);
	}
}



使用方法

通过SQLite类操作数据库的基本代码如下:

void SqliteOperate()
{
    TCHAR *szDbPath = _T("Book.db");

    ::DeleteFile(szDbPath);

    SQLite sqlite;

    // 打开或创建数据库
    //******************************************************
    if(!sqlite.Open(szDbPath))
    {
        _tprintf(_T("%s\n"),sqlite.GetLastErrorMsg());
        return;
    }
    //******************************************************

    // 创建数据库表
    //******************************************************
    TCHAR sql[512] = {0};
    _stprintf(sql,_T("%s"),
        _T("CREATE TABLE [Book] (")
        _T("[id] INTEGER NOT NULL PRIMARY KEY, ")
        _T("[name] NVARCHAR(20), ")
        _T("[author] NVARCHAR(20), ")
        _T("[catagory_id] INTEGER REFERENCES [Category]([id]), ")
        _T("[abstruct] NVARCHAR(100) ,")
        _T("[path] NVARCHAR(50), ")
        _T("[image] BLOB);")
        _T("CREATE INDEX [Book_id] ON [Book] ([id]);")
        );
    if(!sqlite.ExcuteNonQuery(sql))
    {
        printf("Create database table failed...\n");
    }
    //******************************************************

    // 插入数据【普通方式】
    DWORD dwBeginTick = GetTickCount();
    //******************************************************
    // 当一次性插入多条记录时候,采用事务的方式,提高效率
    sqlite.BeginTransaction();
    // 批量插入数据
    for(int i=0;i<1000;i++)
    {
        memset(sql,0,sizeof(sql));
        _stprintf(sql,_T("insert into Book(name,author,catagory_id) values('红高粱%d','莫言',1)"),i);
        if(!sqlite.ExcuteNonQuery(sql))
        {
            _tprintf(_T("%s\n"),sqlite.GetLastErrorMsg());
            break;
        }
    }
    // 提交事务
    sqlite.CommitTransaction();
    printf("Insert Data Take %dMS...\n",GetTickCount()-dwBeginTick);
    //******************************************************


    // 插入数据【通过参数绑定的方式,提交批量数据时,比上面的普通模式效率更高(提高约45%),同时可支持插入二进制数据】
    dwBeginTick = GetTickCount();
    //******************************************************
    // 当一次性插入多条记录时候,采用事务的方式,提高效率
    sqlite.BeginTransaction();
    memset(sql,0,sizeof(sql));
    _stprintf(sql,_T("insert into Book(name,author,catagory_id,image) values(?,'韩寒',?,?)"));
    SQLiteCommand cmd(&sqlite,sql);
    // 批量插入数据
    for(int i=0;i<1000;i++)
    {
        TCHAR strValue[16] = {0};
        _stprintf(strValue,_T("他的国%d"),i);
        // 绑定第一个参数(name字段值)
        cmd.BindParam(1,strValue);
        // 绑定第二个参数(catagory_id字段值)
        cmd.BindParam(2,20);
        BYTE imageBuf[] = {0xff,0xff,0xff,0xff};
        // 绑定第三个参数(image字段值,二进制数据)
        cmd.BindParam(3,imageBuf,sizeof(imageBuf));
        if(!sqlite.ExcuteNonQuery(&cmd))
        {
            _tprintf(_T("%s\n"),sqlite.GetLastErrorMsg());
            break;
        }
    }
    // 清空cmd
    cmd.Clear();
    // 提交事务
    sqlite.CommitTransaction();
    printf("Insert Data Take %dMS...\n",GetTickCount()-dwBeginTick);
    //******************************************************

    // 查询
    dwBeginTick = GetTickCount();
    //******************************************************
    memset(sql,0,sizeof(sql));
    _stprintf(sql,_T("%s"),_T("select * from Book where name = '他的国345'"));

    SQLiteDataReader Reader = sqlite.ExcuteQuery(sql);

    int index = 0;
    int len = 0;
    while(Reader.Read())
    {
        _tprintf( _T("***************【第%d条记录】***************\n"),++index);
        _tprintf( _T("字段名:%s 字段值:%d\n"),Reader.GetName(0),Reader.GetIntValue(0));
        _tprintf( _T("字段名:%s 字段值:%s\n"),Reader.GetName(1),Reader.GetStringValue(1));
        _tprintf( _T("字段名:%s 字段值:%s\n"),Reader.GetName(2),Reader.GetStringValue(2));
        _tprintf( _T("字段名:%s 字段值:%d\n"),Reader.GetName(3),Reader.GetIntValue(3));
        _tprintf( _T("字段名:%s 字段值:%s\n"),Reader.GetName(4),Reader.GetStringValue(4));
        // 读取图片二进制文件
        const BYTE *ImageBuf = Reader.GetBlobValue(6,len);
        _tprintf( _T("*******************************************\n"));
    }
    Reader.Close();
    printf("Query Take %dMS...\n",GetTickCount()-dwBeginTick);
    //******************************************************

    // 关闭数据库
    sqlite.Close();
}


相关文章推荐

SQLite学习笔记之三

一.如何备份数据库 先打开数据库test.db E:\sqlite\tool\sqlite-3_6_22>sqlite3  D:\Test\debug\test.db sqlite> .back...

SQLite学习笔记(6)

准备查询的优点:         不需要回调接口,编码简单、清晰;关联了提供信息的函数,可以获得列的存储类型、声明类型、模式名称、表名、数据库名;sqlite3_exec( )的接口只是提供列的名称...

一个SQLite3的小例子

写了一个比较简单的SQLite3的简单demo只实现了增加 更新 删除 获取全部数据  查找   数据库部分有详细注释    界面也比较简陋主要是实现一下数据库部分 资源地址:http://...

SQLite的使用

最近频繁使用到 SQLite,越来越发现该数据库的好用。SQLite 是一款开源、轻量、本地化存储、功能强大的数据库,由于它的这些特性,使得人们在项目中广泛应用。 首先,你可能要去www.s...

c++ Sqlite 简单使用-查询

BOOL CFileConfigure::LoadSQLiteConfig( const CString& szField, CFieldEx* pField) { USES_CONVERSION;...

sqlite 数据库 boolean类型的小小测试

sqlite数据库中没有没有独的Boolean存储类,,Booean值以整数0(false)和1(true)存储, 经我段时间的实践, boolean 有三种状态, 0(false)  1(true...

sqlite 数据库 对 BOOL型 数据的插入处理

如果 直接 用 insert into tblTest(ID,Name,IsCircled)select 1,'abc','True'  那么对应的 IsCircled字段 还是 FALSE, 正...

Android的使用SQLite

  • 2017-08-11 13:56
  • 217KB
  • 下载

初学Android,数据存储之使用SQLite数据库(四十六)

SQLiteOpenHelper是Android提供的一个管理sqlite数据库的一个工具类主要用于创建一个数据库,并对数据库的版本进行管理。此类为一抽象类,使用是需要继承此类并实现该类的方法 onC...
  • lee576
  • lee576
  • 2012-09-13 17:23
  • 1565
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)