Python与C/C++中的字符串

首先是Python

Python字符串前 加 u、r、b 的含义

1、字符串前加 u

例:u"我是含有中文字符组成的字符串。"

作用:后面字符串以 Unicode 格式 进行编码,一般用在中文字符串前面,防止因为源码储存格式问题,导致再次使用时出现乱码。

PS:不是仅仅是针对中文, 可以针对任何的字符串,代表是对字符串进行。一般英文字符在使用各种编码下,,基本都可以正常解析, 所以一般不带u。但是中文有事会出现问题,就要想以前在学校上机敲代码时候一样,优盘一插,源码一拷贝,一打开,中文部分全成框框乱码了。。。贼尴尬。。。
  
2、字符串前加 r
  例:r"\n\n\n\n\n\n”

作用:声明后面的字符串是普通字符串,相对的,特殊字符串中含有:转义字符 \n \t 什么什么的。

测试效果:
在这里插入图片描述

用途:一般用在 正则表达式、文件绝对地址、等等中。。。

3、字符串前加 b
  作用:python3.x里默认的str是(py2.x里的)unicode, bytes是(py2.x)的str, b”“前缀代表的就是bytes
     python2.x里, b前缀没什么具体意义, 只是为了兼容python3.x的这种写法
     
4、后言:
参考资料:http://blog.csdn.net/u010496169/article/details/70045895

这是另外一个解释的比较清楚的博客的链接,python3中已经不需要太多注意字符串了,因为它是真正意义上的字符串unicode,而只有网络编程以及不同设备之间通信需要注意byte类型以及字节序的问题
https://www.cnblogs.com/liangmingshen/p/9274021.html
加u与加b代表了字符串编码,而加r代表的是对于字符串中的转义字符的处理。
https://blog.csdn.net/weixin_42165585/article/details/80980739

转载请注明出处 (●’◡’●):http://www.cnblogs.com/Twobox/


额外的话题:Python中的单引号,双引号,三引号:
https://www.cnblogs.com/helloworldcc/p/7736961.html
想了解unicode,utf-8以及ascii编码可以看看:
https://www.cnblogs.com/chownjy/p/6625299.html
https://www.cnblogs.com/kingstarspe/p/ASCII.html

最后奉上一份python字符串使用教程:
https://www.cnblogs.com/aylin/p/5452481.html


接下来是C/C++

作为计算机语言,可以说C/C++更加底层,他需要直接面向基本数据类型以及数据编码格式,尽管他们最终都需要编译生成字节码运行(指令与数据最终都是二进制码。。),但是python解释器是由C/C++写的,并且一个是解释型语言,一个是编译型语言。

这里有一个介绍C/C++比较好的网站:
http://c.biancheng.net/view/208.html
一般在C里面我们我往往使用字符数组char来处理字符串,而C++拓展了这一规范,这个博客充分说明了这一点。。

最后再来看一个共性的问题:
关于C里面 数字0 ,字符‘0’ ,字符串“0”,字符串结尾标示符‘\0’
参考:
https://blog.csdn.net/sungaochao/article/details/41726715

也就是说,
字符代表的是意义,在计算机中是以数字表达的,这其中就需要一套编码规范,而这套编码规范中也包含了对于数字(0-9)的编码,更大数字当字符串对待,由0-9组合即可。**

用代码可以直观看出字符0、数字0和‘\0’的区别:
https://www.cnblogs.com/shelmean/p/9949490.html


额外说几点吧。
https://www.cnblogs.com/shelmean/p/9949490.html

字符0、数字0和‘\0’的区别在这里插入图片描述

ASCII码值 0 表示空字符,空字符就是平时所说的 ‘\0’。
字符 ‘0’,ASCII码值为 48,如:“012” 字符串中的 0 表示字符 ‘0’。
数字 0, 所说的数字 0,就是平时说的十进制数字 0,其ASCII码为 0,在字符串中表示 ‘\0’,即空字符。

#include <stdio.h>
#include <string.h>
 
int main()
{
    char array[4] = {0};
    array[0] = '0';
    array[1] = 0;
    array[2] = '\0';
    array[3] = 48;
    printf("array[0] = %d\n", array[0]);//字符0: '0' <==> 48(ASCII)
    printf("array[1] = %d\n", array[1]);
    printf("array[2] = %d\n", array[2]);
    printf("array[3] = %d\n", array[3]);                           
 
    printf("array[0] = %c\n", array[0]);//字符0
    printf("array[1] = %c\n", array[1]);//空字符 0(ASCII)
    printf("array[2] = %c\n", array[2]);//空字符 '\0'
    printf("array[3] = %c\n", array[3]);//字符0: 48(ASCII) <==> '0'
 
    return 0;
}

输出:

$ ./a.out

array[0] = 48
array[1] = 0
array[2] = 0
array[3] = 48
array[0] = 0
array[1] = 
array[2] = 
array[3] = 0

推荐一个很好的博客:
http://www.cnblogs.com/Twobox/
各种骚操作。。
感觉这个视频应该很好,但是需要会员,从内存使用的角度帮助理解指针的作用:
https://edu.csdn.net/course/detail/2344
这个人应该想说点有用的东西的,只可惜这个排版不太好看:
https://blog.csdn.net/qq_35710556/article/details/79824543


最后说下C++字符编码的问题,虽然这个并不常用,相比Python的话。

原文:
https://www.cnblogs.com/wynn518/p/8981203.html

C++之编码问题(Unicode,ASCII,本地默认)

本篇文章试图回答的问题:

1、char* pStr=“我aa”;这句代码执行后,pStr指向的内存区域中存储的字节到底是根据什么码表而来的呢?该字符串占几个字节?

2、将一个VS2010的Windows程序设置了“使用Unicode字符集”到底意味着什么?

3、现在有一个文件,其存储内容未知(可能是文本,可能是图像,可能是视频),要求是:在文件最前面插入一串Unicode文本,插入完成后以文本程序打开该文件,插入的文本不会显示为乱码(该文件本身的内容不考虑)。——如何做到?

本人能力、精力有限,所言所感都基于自身的实践和有限的阅读、查阅,如有错误,欢迎拍砖,敬请赐教——博客园:钱智慧。

一:

用VS2010新建一个win32控制台应用程序TestChar,代码如下:

#include <iostream>
using namespace std;
int main()
{
    char* pStr="我aa";
    cout<<sizeof("我aa")<<endl;
    cout<<hex<<pStr[0]-0;
    cout<<pStr[1]-0<<endl;
    return 0;
}

打印结果如下:
在这里插入图片描述

分析:存储介质(内存、外存等)上存储的都是二进制数据,而对于字符信息的存储,先查码表进行解码,再把码以二进制信息存储,即pStr指向的这段内存中存储的都是字符们的编码:一个中文字符,一个英文字母,一个全角字母。要得到”我“的编码,默认会查找本地码表,本人是中文Win7系统,查找的是GB2312码表,而对照GB2312码表可发现,"我"的编码正是ced2,与打印一致(f是符号位,可无视)。另外GB2312是不会对英文字母进行编码的,因为英文字母属于半角字符,这类编码由ASCII码表负责,GB2312中的任何字符都占用两个字节,空字符也由ASCII负责编码,这就是为何上面的字符串占用的字节数目是6。可见,上面一句代码,其实涉及到了两张码表:ASCII码表和GB2312。

二:

新建一个MFC对话框程序,名为TestUnicodeChar。默认情况下,VS2010建的项目都是基于Unicode的,即打开项目的属性,在”字符集“设置中都是”使用Unicode字符集“,这到底是什么意思呢?莫非在程序中用的中文都是以Unicode字节进行存储的?由上面的TestChar程序可以看出并非如此,pStr中的"我”是以GB2312进行存储的。又或者,源文件(h文件、cpp文件等)在磁盘上是以Unicode进行编码存储的?也不是,源文件存储默认也是以本地GB2312进行存储的。验证方式:用UE编辑器随便打开一个设置了“使用Unicode字符集”项目的某个源文件,比如打开本项目中的TestUnicodeCharDlg.cpp文件,在UE中以16进制的形式查看该文件内容,可以发现其前两个字节并非FF FE(这是Unicode文件的标识),你随便找一个中文,对照其16进制找到其编码,然后跟GB2312码表中的该中文的编码对照即可验证。这是一个GB2312码表的网页链接:

http://tool.xker.com/gb2312tbl.php

我们知道,在C++中,有char和string,为了支持Unicode字符,还有wchar_t和wstring,我们可以认为string是基于char的,而wstring是基于wchar_t的。为什么要引入Unicode呢,只用char和string难道不能保存含有中文的字符(串)吗?由TestChar程序,我们知道完全可以,并且采用的是GB2312编码,问题是你无法确定一个类似pStr的混合串的字符数目,比如:string str=“我a”,你调用string的length方法不能准确得到str的长度,这给编程带来了不便。所以你可以选用wstring和wchar:wstring wstr=L"我a",这时你再调用wstring的length方法就能准确得到了,其中L前缀可以使后面紧跟的字符串解释成宽字符串(Unicode)。因为Unicode对任何字符都采用2个字节进行编码,所以length的实现想必也很简单:每两个字节算一个字符,进而可以方便得到字符串的长度,这便是Unicode优于多字节编码的地方:试想,如果采用多字节编码,那么要实现基于这种编码的字符串类的length方法会非常头疼。Unicode浪费了存储空间但带来了编程上的简便。(关于这方面的详细内容可以参考《Windows程序设计 第5版》第1章)

在Windows中,有这样一些宏:_T,TEXT,TCHAR,CString,它们根据不同的设定有不同的含义。先看一下它们的使用:

在TestUnicodeCharDlg.cpp的OnPaint方法中加上如下代码:

1 TCHAR * pStr=TEXT("a我");//_T与TEXT的含义是一样的
2 CString str1=TEXT("a我");
3 CString str2=L"a我";

如果程序定义了UNICODE宏,则_T和TEXT(二者含义和用法完全一样)便会把括号内的字符串解释为UNICODE字符串,TCHAR便会替换为wchar_t,CString便会替换为CStringW,否则(即没有定义UNICODE宏),都会解释为相应的char版本。而L前缀不是宏,类似强转:不管有没有定义UNICODE宏,都把后面的字符串解释为UNICODE字符串。而设置“使用UNICODE字符集”就相当于定义UNICODE宏,即该设置仅仅是影响了一些宏的行为。若把该项目的“使用Unicode字符集”设置改为“未使用”,则编译会出错,因为此时str2就是一个CStringA实例,你不能把一个L前缀的字符串(Unicode字符串)赋值给它。

三:

第三个问题本质上就是往一个文件中写Unicode字符串的问题。涉及到编码问题的文件操作始终牢记一点:以什么编码写,就以编码读。在TestUnicodeChar程序的OnInitDialog函数中加入如下代码:

BOOL CTestUnicodeCharDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码
    CFile myFile;


    if ( myFile.Open( _T("c:\\myfile.txt"), CFile::modeCreate |   
        CFile::modeReadWrite ) )
    {

        CString str=TEXT("a我");
        //myFile.Write("\xff\xfe",2);
        myFile.Write( str, str.GetLength()*sizeof(TCHAR) ); 
        myFile.Flush();
    }
    

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

运行程序,然后用写字板、记事本、NotePad++、UE(UEdit)分别打开mfile.txt发现,有的能正常显示,有的则乱码。要知道,你往myfile.txt写进去的是 两个字符的Unicode编码,用某文本程序去打开myfile.txt,倘若该程序默认情况下读取文本时是按Unicode来解析的,则不会乱码,否则就乱码。我们把注释的那行代码的注释拿掉,用任何支持Unicode的文本程序去打开myfile.txt就不会出错了,因为一个文本中的前两个字节FF FE便向试图打开该文本的程序表明该文本应该用Unicode进行解析(你可以用NotePad++新建几个Unicode格式的文本,随便保存几个字符,然后用UE以16进制格式查看便可知Unicode文本的前两个字节都是FF FE)。

另外,经常遇到有人问这样的问题:CString如何转换为char*?问这个问题之前,最好问下自己:我的目的是什么,为何要进行这样的转换,当前项目有没有设置Unicode。要知道,如果设置了Unicode,则CString存储的是Unicode字符串,转换为char后,你如果直接显示这个char或者写到文件中(没有把FF FE写到文件开始处)然后打开,则会(假如打开文件的程序默认不以Unicode进行解析)出现乱码,所以,这种情况下,转换为char的意义不大——这不是说不能把Unicode串转为char,这完全是可行的,本质上这只是在把一个Unicode字符串的内存内容"活生生”取出来而已。不管怎样,下面的代码重新修改了OnInitDialog函数,演示了几种情况:

BOOL CTestUnicodeCharMFCDlg::OnInitDialog()
{
    CDialogEx::OnInitDialog();

    // 将“关于...”菜单项添加到系统菜单中。

    // IDM_ABOUTBOX 必须在系统命令范围内。
    ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
    ASSERT(IDM_ABOUTBOX < 0xF000);

    CMenu* pSysMenu = GetSystemMenu(FALSE);
    if (pSysMenu != NULL)
    {
        BOOL bNameValid;
        CString strAboutMenu;
        bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
        ASSERT(bNameValid);
        if (!strAboutMenu.IsEmpty())
        {
            pSysMenu->AppendMenu(MF_SEPARATOR);
            pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
        }
    }

    // 设置此对话框的图标。当应用程序主窗口不是对话框时,框架将自动
    //  执行此操作
    SetIcon(m_hIcon, TRUE);            // 设置大图标
    SetIcon(m_hIcon, FALSE);        // 设置小图标

    // TODO: 在此添加额外的初始化代码
    CFile myFileW,myFileA,myFileCharArrow,myFileWOrA;


    if ( myFileW.Open( _T("c:\\myfileW.txt"), CFile::modeCreate |   
        CFile::modeReadWrite )  && 
        myFileA.Open( _T("c:\\myfileA.txt"), CFile::modeCreate |   
        CFile::modeReadWrite ) &&
        myFileCharArrow.Open( _T("c:\\myfileCharArrow.txt"), CFile::modeCreate |   
        CFile::modeReadWrite ) &&
        myFileWOrA.Open( _T("c:\\myFileWOrA.txt"), CFile::modeCreate |   
        CFile::modeReadWrite ))
    {

        CString strW=TEXT("a我");//因为本项目设置了Unicode字符集,所以我们知道CString会被替换为CStringW
        
        CStringA strA(strW);
        

        myFileW.Write( strW, strW.GetLength()*2); 
        myFileW.Flush();

        myFileA.Write(strA,strA.GetLength());
        myFileA.Flush();
        //CString的GetString返回的const类型指针,要么在右边强转,要么左边用const类型的char*去接
        //注意指针命名:p是pointer,c是const,如果有t则是TEXT,w是wide,l是long
        char* pstr=(char*)strA.GetString();
        /*下面这行代码若不注掉,会报错,因为本程序是Unicode程序,
        strW会是一个CStringW类型的字符串,它的GetString返回的是LPCWSTR类型指针
        当然不能赋值给LPCSTR类型指针了,一个是const wchar_t*,另一个是const char*
        */
        //const char* pcstr1=strW.GetString();
        
        myFileCharArrow.Write(pstr,strlen(pstr));
        myFileCharArrow.Flush();

        //假设我们在编程中,不知道有没有使用Unicode设置,为了通用,我们可以尽量使用宏及通用版本的相关函数(如_tcslen)
        CString strWOrA=TEXT("a我");
        //注意这里的TCHAR不一定就是wchar_t,这取决于程序是否设置了Unicode
        const TCHAR* pctstr=strWOrA.GetString();//CString的GetString返回的是const指针
        myFileWOrA.Write(ptstr,_tcslen(pctstr)*sizeof(TCHAR));
        myFileWOrA.Flush();

    }
    

    return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
}

用UE察看几个文件的内容,如图:
在这里插入图片描述

其中,CED2是”我“的GB2312编码,6211(注意字节高低次序)是”我“的Unicode编码,我们可知,CStringA strA(strW)这行代码,一定进行了码表间的转换。结合代码,文件内容应该不难理解。

还有些让人容易头晕的字符串指针宏,下面列举出来:

关于char*的:

LPCSTR: long pointer const string,可看成const char*,与PCSTR相似

LPSTR:可看成char*,与PSTR相似

关于wchar_t*的:

LPCWSTR,PCWSTR,LPWSTR,PWSTR

通用版本(根据是否配置了Unicode有不同的宏替换):

TCHAR*

LPTSTR,LPCTSTR (T有点类似TEXT宏的意思)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值