base64网上实现很多,但是如果是对中文进行编码,有的无法编码,有的编码结果不一致

经过研究,发现base64算法都没有问题,问题出现在汉字的编码上,下面的base64编码稍微做了一些改进,增加了编码判断

所有汉字一律转换成UTF8后再进行base64编码,与网络上通用的base64解码接轨。

以下base64算法使用了开源库uchardet,需要下载uchardet源码编译生成动态库调用

uchardet源码网址:官网地址

/*** base64编码原理* 1. 源数据都是8位位宽的数据;* 2. 相当于分组码,将源数据分为3个一组,每一组共24bits,采用每6位对应一个编码码字,那么3*8bits = 4*6its,将3个数据映射成4个数据,由于编码的码字都是6位长度,换位10进制就是0-63,总共有64中可能性,这也是base64名字的来历;* 3. 6bits对应10进制数对应的码字如表;[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/]*  转解码过程*  3 * 8 = 4 * 6; 3字节占24位, 4*6=24*  先将要编码的转成对应的ASCII值*  如编码: s 1 3*  对应ASCII值为: 115 49 51*  对应二进制为: 01110011 00110001 00110011*  将其6个分组分4组: 011100 110011 000100 110011*  而计算机是以8bit存储, 所以在每组的高位补两个0如下:*  00011100 00110011 00000100 00110011对应:28 51 4 51*  查找base64 转换表 对应 c z E z*  *  解码*  c z E z*  对应ASCII值为 99 122 69 122*  对应表base64_suffix_map的值为 28 51 4 51*  对应二进制值为 00011100 00110011 00000100 00110011*  依次去除每组的前两位, 再拼接成3字节*  即: 01110011 00110001 00110011*  对应的就是s 1 3*/

#ifndef __BASE64_H_
#define __BASE64_H_/********************************************************Func Name: base64_encode
Date Created: 2018-8-3Description: base64编码Input: plaintext_in:源文件length_in:源文件长度Output:     code_out:生成编码文件length_out:生成编码文件的长度Return: Caution: code_out内存由调用函数释放
*********************************************************/
int base64_encode(const char *plaintext_in, int length_in, char **code_out, int *length_out);/********************************************************Func Name: base64_decode
Date Created: 2018-8-3Description: base64解码Input:       code_in;编码后的文件length_in:编码后的文件长度Output: plaintext_out:源文件outlen:源文件长度Return: Caution: plaintext_out内存由调用函数释放
*********************************************************/
int base64_decode(char *code_in, int length_in, char **plaintext_out, int *outlen);/********************************************************Func Name: base64_encode_calculate
Date Created: 2018-8-2Description: 编码算法Input: plaintext_in:源文件length_in:源文件长度Output:     code_out:生成编码文件length_out:生成编码文件的长度Return: Caution: code_out内存由调用函数释放
*********************************************************/
int base64_encode_calculate(const char *plaintext_in, int length_in, char **code_out, int *length_out);/********************************************************Func Name: base64_decode_calculate
Date Created: 2018-8-3Description: 解码算法Input:       code_in;编码后的文件length_in:编码后的文件长度Output: plaintext_out:源文件outlen:源文件长度Return: Caution: plaintext_out内存由调用函数释放
*********************************************************/
int base64_decode_calculate(char *code_in, int length_in, char **plaintext_out, int *outlen);#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "base64.h"
#include "ucharcode.h"#define SRC_CHAR_SIZE 3                //源码3个字节
#define BASE_CHAR_SIZE 4               //编码后4个字节
#define CHAR_SIZE 8                    //一个字节有8bits
#define BASE_DATA_SIZE 6               //base编码中6个bits是实际数据#define DEFAULT_CODE "UTF-8"/********************************************************Func Name: base64_encode_value
Date Created: 2018-8-2Description: 获取对应编码的值Input: value_in:需要编码的字符Output: Return: Caution:
*********************************************************/
char base64_encode_value(char value_in)
{static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";if (value_in > 63) return '=';return encoding[(int)value_in];
}/********************************************************Func Name: base64_decode_value
Date Created: 2018-8-2Description: 获取对应解码的值Input: value_in:需要解码的字符Output: Return: Caution:
*********************************************************/
int base64_decode_value(char value_in)
{static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};static const char decoding_size = sizeof(decoding);//+ 的ascll值是43value_in -= 43;if (value_in < 0 || value_in >= decoding_size) return -1;return decoding[(int)value_in];
}/********************************************************Func Name: base64_encode
Date Created: 2018-8-3Description: base64编码Input: plaintext_in:源文件length_in:源文件长度Output:     code_out:生成编码文件length_out:生成编码文件的长度Return: Caution: code_out内存由调用函数释放
*********************************************************/
int base64_encode(const char *plaintext_in, int length_in, char **code_out, int *length_out)
{int iRet = 0;char * pcCode = NULL;char *pcOut = NULL;//因为utf8一个汉字用3个字节表示,gdk用两个字节表示,所以2倍的长度应该是够用的int iOutLen = length_in * 2;int iLeftNum = iOutLen;//参数校验if (NULL == plaintext_in || 0 == length_in || NULL == code_out || NULL == length_out){return -1;}//编码格式判断iRet = getStringCode(plaintext_in, length_in, &pcCode);if(0 != iRet){return -2;}if (0 == strcmp(pcCode,DEFAULT_CODE)){iRet = base64_encode_calculate(plaintext_in, length_in, code_out, length_out);return iRet;}//如果不是UTF-8编码 需要转码pcOut = (char *)malloc(iOutLen);if (NULL == pcOut){return -3;}memset(pcOut, 0, iOutLen);iRet = transcodeToUTF8((char *)plaintext_in, length_in, pcOut, &iLeftNum, pcCode);if (0 != iRet){//释放资源if (pcOut){free(pcOut);pcOut = NULL;}return -4;}//释放编码资源if (pcCode){free(pcCode);pcCode = NULL;}iRet = base64_encode_calculate(pcOut, iOutLen - iLeftNum, code_out, length_out);//释放资源if (pcOut){free(pcOut);pcOut = NULL;}return iRet;}/********************************************************Func Name: base64_encode_calculate
Date Created: 2018-8-2Description: 编码算法Input: plaintext_in:源文件length_in:源文件长度Output:     code_out:生成编码文件length_out:生成编码文件的长度Return: Caution: code_out内存由调用函数释放
*********************************************************/
int base64_encode_calculate(const char *plaintext_in, int length_in, char **code_out, int *length_out)
{int iPadLen = 0;                 //需要补齐的字节数int iBaseLen = 0;                //base64编码后的字节数int i = 0;char *pcOut = NULL;char gPadChar[BASE_CHAR_SIZE] = {0};char * pcOutIndex = NULL;if (NULL == plaintext_in || 0 == length_in || NULL == code_out || NULL == length_out){return -1;}if (0 != length_in % SRC_CHAR_SIZE){//3 - length_in%3 源码需要添加的字节数iPadLen = SRC_CHAR_SIZE - length_in % SRC_CHAR_SIZE;}//计算base编码后实际长度 +1 最后一个字符是'/0'iBaseLen = (length_in + iPadLen)* CHAR_SIZE / BASE_DATA_SIZE + 1;pcOut = (char *)malloc(sizeof(char) * iBaseLen);if (NULL == pcOut){return -2;}memset(pcOut, 0, sizeof(char) * iBaseLen);pcOutIndex = pcOut;for (i = 0; i < length_in; i += SRC_CHAR_SIZE){if (i == (length_in + iPadLen -3) && 0 != iPadLen){if (1 == iPadLen){//末尾实际上有两个字节的数据,将这两个字节的数据进行编码//第一个字节处理gPadChar[0] = base64_encode_value(*(plaintext_in+i) >> 2 & 0x3f);//第二个字节处理gPadChar[1] = base64_encode_value((*(plaintext_in+i) << 6 >> 2 & 0x30) + (*(plaintext_in+i+1) >> 4 & 0xf));//第二个字节最后4bits处理,不足的补0gPadChar[2] = base64_encode_value((*(plaintext_in+i+1) << 4 >> 2 & 0x3c));//没有字节的以'='代替 因为base编码只有6bit真实数据,所以不会和源数据中的"="重复gPadChar[3] = '=';}else if (2 == iPadLen){//末尾实际上有一个字节的数据,将这一个字节的数据进行编码//第一个字节处理gPadChar[0] = base64_encode_value(*(plaintext_in+i) >> 2 & 0x3f);//第一个字节最后2bits处理gPadChar[1] = base64_encode_value((*(plaintext_in+i) << 6 >> 2 & 0x30));gPadChar[2] = '=';gPadChar[3] = '=';}}else{//第一个字节处理  0x3f前两位清零//取第一个字节的前6bitsgPadChar[0] =base64_encode_value(*(plaintext_in+i) >> 2 & 0x3f);//第二个字节处理//*(pcIndex+i) << 6 >> 2 & 0x30 取第一个字节的后2bits 无效数据清零//*(pcIndex+i+1) >> 4 & 0xf 取第二个字节的前4bits 无效数据清零gPadChar[1] = base64_encode_value((*(plaintext_in+i) << 6 >> 2 & 0x30) + (*(plaintext_in+i+1) >> 4 & 0xf));//第二个字节和第三个字节处理//*(pcIndex+i+1) << 4 >> 2 & 0x3c 取第二个字节的后4bits数据 无效数据清零//(*(pcIndex+i+2) >> 6 & 0x3 取第三个字节的前2bits数据 无效数据清零gPadChar[2] = base64_encode_value((*(plaintext_in+i+1) << 4 >> 2 & 0x3c) + (*(plaintext_in+i+2) >> 6 & 0x3));//第三个字节处理//*(pcIndex+i+2) & 0x3f  清除第三个字节的前2bits数据gPadChar[3] = base64_encode_value(*(plaintext_in+i+2) & 0x3f);}//并非字符串操作,不能使用strcat
        memcpy(pcOutIndex, gPadChar, BASE_CHAR_SIZE);pcOutIndex += BASE_CHAR_SIZE;memset(gPadChar, 0, BASE_CHAR_SIZE);}pcOut[iBaseLen-1] = 0;*length_out = iBaseLen;*code_out = pcOut;return 0;
}/********************************************************Func Name: base64_decode
Date Created: 2018-8-3Description: base64解码Input:       code_in;编码后的文件length_in:编码后的文件长度Output: plaintext_out:源文件outlen:源文件长度Return: Caution: plaintext_out内存由调用函数释放
*********************************************************/
int base64_decode(char *code_in, int length_in, char **plaintext_out, int *outlen)
{int iRet = base64_decode_calculate(code_in, length_in, plaintext_out, outlen);return iRet;
}/********************************************************Func Name: base64_decode_calculate
Date Created: 2018-8-3Description: 解码算法Input:       code_in;编码后的文件length_in:编码后的文件长度Output: plaintext_out:源文件outlen:源文件长度Return: Caution: plaintext_out内存由调用函数释放
*********************************************************/
int base64_decode_calculate(char *code_in, int length_in, char **plaintext_out, int *outlen)
{int i = 0, j = 0;int iPadNum = 0;char *pcSrc = code_in;char * pcIndex = NULL;int iSrcLen = 0;char *pcOut = NULL;if (NULL == code_in || NULL == plaintext_out || NULL == outlen){return -1;}while(1){pcIndex = strchr(pcSrc, '=');if (NULL == pcIndex){break;}iPadNum++;pcIndex += 1;pcSrc = pcIndex;}//计算源文件的字符个数iSrcLen = length_in/4*3 - iPadNum;//末尾增加\0pcOut = (char *)malloc(sizeof(char)*iSrcLen + 1);if (NULL == pcOut){return -2;}memset(pcOut, 0, sizeof(char)*iSrcLen + 1);for (i = 0, j = 0; i < length_in; i += 4){if ((i == length_in-4) && iPadNum > 0){if (1 == iPadNum){//实际上有两个字符 只能用3个base字符表示//字符1pcOut[j] = (base64_decode_value(code_in[i]) << 2) + (base64_decode_value(code_in[i+1]) << 2 >> 6 & 0x3);//字符2pcOut[j+1] = (base64_decode_value(code_in[i+1]) << 4) + (base64_decode_value(code_in[i+2]) << 2 >> 4 & 0xf);j += 2;}else if (2 == iPadNum){//实际上有1个字符数据 只能用2个base字符表示pcOut[j] = (base64_decode_value(code_in[i])<<2) + (base64_decode_value(code_in[i+1]) << 2 >> 6 &0x3);j ++;}}else{//字符1pcOut[j] = (base64_decode_value(code_in[i])<<2) + (base64_decode_value(code_in[i+1]) << 2 >> 6 &0x3);//字符2pcOut[j+1] = (base64_decode_value(code_in[i+1]) << 4) + (base64_decode_value(code_in[i+2]) << 2 >> 4 & 0xf);//字符3pcOut[j+2] = (base64_decode_value(code_in[i+2]) << 6) + (base64_decode_value(code_in[i+3]) & 0x3f);j += 3;}}pcOut[iSrcLen] = '\0';*plaintext_out = pcOut;*outlen = iSrcLen;return 0;
}

#ifndef __UNCHARCODE_H_
#define __UNCHARCODE_H_/********************************************************Func Name: getStringCode
Date Created: 2018-8-3Description: 获取字符串编码Input: pcSrc:源编码数据iLenIn:源编码长度pcCode:结果存放内存地址Output:         Return: error codeCaution: pcDest内存需要由调用函数释放
*********************************************************/
int getStringCode(const char *pcSrc, int iLenIn, char **pcCode);/********************************************************Func Name: transcodeToUTF8
Date Created: 2018-8-3Description: 转码UTF8Input: pcSrc:源编码数据lenIn:源编码长度pcDest:结果存放内存地址(INOUT)lenOut:剩余内存单位个数(INOUT)pcCodeType:源编码类型Output:         Return: error codeCaution: pcDest内存需要由调用函数分配
*********************************************************/
int transcodeToUTF8(char *pcSrc, int lenIn, char *pcDest, int *lenOut,const char *pcCodeType);/********************************************************Func Name: transcodeToGBK
Date Created: 2018-8-3Description: 转码GBKInput: pcSrc:源编码数据lenIn:源编码长度pcDest:结果存放内存地址(INOUT)lenOut:剩余内存单位个数(INOUT)pcCodeType:源编码类型Output:         Return: error codeCaution: pcDest内存需要由调用函数分配
*********************************************************/
int transcodeToGBK(char *pcSrc, int lenIn, char *pcDest, int *lenOut,const char *pcCodeType);#endif

#include <stdlib.h>
#include <string.h>
#include <iconv.h>#include "ucharcode.h"
#include "uchardet.h"/********************************************************Func Name: transcodeToUTF8
Date Created: 2018-8-3Description: 转码UTF8Input: pcSrc:源编码数据lenIn:源编码长度pcDest:结果存放内存地址(INOUT)lenOut:剩余内存单位个数(INOUT)pcCodeType:源编码类型Output:         Return: error codeCaution: pcDest内存需要由调用函数分配
*********************************************************/
int fromcodeToCode(char *pcSrc, int lenIn, char *pcDest, int *lenOut, const char *pcFromCode, const char *pcToCode)
{int iRet = 0;//由于iconv()函数会修改指针,所以要保存源指针char *pcStart = pcSrc;char *pcOut = pcDest;iconv_t cd = (iconv_t)-1;size_t inbytesleft = (size_t)lenIn;if (NULL == pcSrc || 0 >= lenIn || NULL == pcDest || NULL == lenOut || NULL == pcFromCode || NULL == pcToCode){return -1;}cd = iconv_open(pcToCode, pcFromCode);if ((iconv_t)-1 == cd){return -2;}/* *@param cd iconv_open()产生的句柄*@param pcStart 需要转换的字符串*@param inbytesleft 存放还有多少字符没有转换*@param pcOut 存放转换后的字符串*@param outlen 存放转换后,pcOut剩余的空间*/iRet = iconv(cd, &pcStart, &inbytesleft, &pcOut, (size_t *)lenOut);if (iRet < 0){return -3;}iconv_close(cd);return iRet;
}/********************************************************Func Name: transcodeToUTF8
Date Created: 2018-8-3Description: 转码UTF8Input: pcSrc:源编码数据lenIn:源编码长度pcDest:结果存放内存地址(INOUT)lenOut:剩余内存单位个数(INOUT)pcCodeType:源编码类型Output:         Return: error codeCaution: pcDest内存需要由调用函数分配
*********************************************************/
int transcodeToUTF8(char *pcSrc, int lenIn, char *pcDest, int *lenOut,const char *pcCodeType)
{int iRet = 0;const char * targetCode = "UTF8";if (NULL == pcSrc || 0 >= lenIn || NULL == pcDest || NULL == lenOut || NULL == pcCodeType){return -1;}iRet = fromcodeToCode(pcSrc, lenIn, pcDest, lenOut, pcCodeType, targetCode);return iRet;
}/********************************************************Func Name: transcodeToGBK
Date Created: 2018-8-3Description: 转码GBKInput: pcSrc:源编码数据lenIn:源编码长度pcDest:结果存放内存地址(INOUT)lenOut:剩余内存单位个数(INOUT)pcCodeType:源编码类型Output:         Return: error codeCaution: pcDest内存需要由调用函数分配
*********************************************************/
int transcodeToGBK(char *pcSrc, int lenIn, char *pcDest, int *lenOut, char *pcCodeType)
{int iRet = 0;const char * targetCode = "GBK";if (NULL == pcSrc || 0 >= lenIn || NULL == pcDest || NULL == lenOut || NULL == pcCodeType){return -1;}iRet = fromcodeToCode(pcSrc, lenIn, pcDest, lenOut, pcCodeType, targetCode);return iRet;
}/********************************************************Func Name: getStringCode
Date Created: 2018-8-3Description: 获取字符串编码Input: pcSrc:源编码数据iLenIn:源编码长度pcCode:结果存放内存地址Output:         Return: error codeCaution: pcDest内存需要由调用函数释放
*********************************************************/
int getStringCode(const char *pcSrc, int iLenIn, char **pcCode)
{uchardet_t ud;int iErrorCode = 0;char *pcBuf = NULL;const char *pcDest = NULL;if (NULL == pcSrc || 0 == iLenIn || NULL == pcCode){return -1;}ud = uchardet_new();//如果样本字符不够,那么有可能导致分析失败iErrorCode = uchardet_handle_data(ud, pcSrc, iLenIn);if (0 != iErrorCode){return -2;}uchardet_data_end(ud);pcDest = uchardet_get_charset(ud);//+1 多留一个字符'\0'pcBuf = (char *)malloc(strlen(pcDest)+1);if (NULL == pcBuf){return -3;}memset(pcBuf, 0, strlen(pcDest)+1);strcpy(pcBuf,pcDest);*pcCode = pcBuf;uchardet_delete(ud);return 0;
}

#include <iostream>
#include <cstring>#include "base64.h"using namespace std;/* 测试小程序 */void test()
{int len = 0;int iRet= 0;char *pcOut = NULL;const char* s = "我是中国人";char * pcBuf = NULL;int lastLen = 0;//base64编码iRet = base64_encode((char *)s, strlen(s), &pcOut, &len);if (0 != iRet){cout << "base64_encode() error ." << endl;}cout << "result = " << pcOut << endl;//base64解码iRet = base64_decode(pcOut, len, &pcBuf, &lastLen);if (0 != iRet){cout << "base64_decode() error ." << endl;}cout << "end len = " << lastLen << "  result = " << pcBuf << endl;if (pcOut){free(pcOut);pcOut = NULL;}if (pcBuf){free(pcBuf);pcBuf = NULL;}
}int main()
{test();getchar();return 0;
}

转载于:https://www.cnblogs.com/zhanggaofeng/p/9416958.html

Linux共享库 base64库相关推荐

  1. linux共享库位置配置(LD_LIBRARY_PATH环境变量 或者 更改/etc/ld.so.conf)

    linux共享库位置配置(LD_LIBRARY_PATH环境变量 或者 更改/etc/ld.so.conf) 转载于:https://www.cnblogs.com/RichardLee/archiv ...

  2. linux共享库 == windows动态库

    linux共享库  == windows动态库     之间基本等同.

  3. Linux共享库路径配置

    Linux共享库路径配置 Linux下找不到共享库文件的典型现象为明明已经安装某个软包(如libnet,MySQL),编译链接可以正常进行,但是在运行时出现如"error while loa ...

  4. linux共享库及/etc/ld.so.conf文件的应用

    Linux 共享库 Linux 系统上有两类根本不同的 Linux 可执行程序.第一类是静态链接的可执行程序.静态可执行程序包含执行所需的所有函数 -换句话说,它们是"完整的".因 ...

  5. Linux 共享库:LD_LIBRARY_PATH 与ld.so.conf_爱过了就好_新浪博客

    Linux 共享库:LD_LIBRARY_PATH 与ld.so.conf_爱过了就好_新浪博客 Linux 共享库:LD_LIBRARY_PATH 与ld.so.conf     (2009-07- ...

  6. UNIX 环境高级编程(二)—— linux共享库,/etc/ld.so.conf 及 ld.so.conf.d/libc.conf

    1. Linux 共享库(/etc/ld.so.conf) 这个文件记录了编译时使用的动态链接库的路径.默认情况下,编译器只会使用 (1)/lib. (2)/usr/lib 这两个目录下的库文件. 如 ...

  7. Linux共享库概述

    Linux共享库概述 共享库是一种将库函数打包成一个单元使之能够在运行时被多个进程共享的技术.这种技术能够节省磁盘空间和RAM. 在继续阐述共享库之前,先来说说静态库,它是比共享库更早的存在.静态库也 ...

  8. linux 共享内存操作(shm_open、mmap、编译链接库:-lz -lrt -lm -lc都是什么库)

    文章目录 linux 共享内存操作(shm_open) 一.背景 二.函数使用说明 shm_open ftruncate(改变文件大小) mmap共享内存 三.示例代码 创建内存共享文件并写入数据 打 ...

  9. 【Linux基础】静态库和共享库(如何自定义库文件?)

    文章目录 前言 一.库文件的基本知识 1.库文件分类 2.库文件命名 二.如何自定义静态库? 1.静态库简介 2.具体流程(详细步骤) 3.静态库缺点 三.如何自定义共享库? 1.共享库简介 2.具体 ...

最新文章

  1. c++解释模式interpreter
  2. kaggle notebook在git push时附带用户民和密码(一行搞定,全部写在一行中)
  3. 国内外软件开发上的差距与分析
  4. python显示no matching distribution,Python使用pip安装No matching distribution found for PyYaml==5.3.1...
  5. 经典同步问题一——生产者和消费者问题
  6. Centos使用yum极速安装Java 1.8
  7. springboot+自定义注解实现灵活的切面配置
  8. 输入法设置,SublimeTest,putty掉线
  9. 4台服务器集群搭建_Redis Cluster高可用集群搭建
  10. JavaScript 学习笔记4
  11. 190723每日一句 学会调整你的心态
  12. html网页设计课程心得,终于发现学习网页设计心得体会
  13. ad9原理图转到orcad capture16.5
  14. 在字节跳动“混”了2年软件测试岗,被辞之后我承认我后悔了...
  15. HTML学习笔记4:如何给网页添加图片和超链接
  16. 解决catkin_make时出现make[2]: *** No rule to make target ‘/usr/lib/libOpenNI2.so‘, needed by ‘*******‘。
  17. 动手学习数据分析第一章内容
  18. 互联网日报 | 1月19日 星期二 | 腾讯音乐全资收购懒人听书;字节跳动整合硬件业务专注教育硬件;PSA与FCA正式完成合并...
  19. 痞子衡嵌入式:盘点国内RISC-V内核MCU厂商(2018年发布产品)
  20. ‘0’ 和 '\0'

热门文章

  1. python怎么读取txt文件数据保存数组中-python将txt等文件中的数据读为numpy数组的方法...
  2. python怎么写文件-Python 读写文件
  3. python天天学怎么样-每天一遍,好好学习,天天向上(Python)
  4. 西安python工资怎么样-西安Python和人工智能的薪资前景到底怎么样?
  5. 零基础python书籍推荐-非IT行业,零基础自学Python,选什么书?
  6. python自学攻略-你是如何自学 Python 的?
  7. python处理excel字典-使用Python代码处理Excel
  8. php python-10分钟从PHP到Python
  9. python流程控制-详解Python流程控制语句
  10. 学python用什么系统好-Python用什么系统环境好?老男孩Python