精华内容
下载资源
问答
  • crnn(基于pytorch、python3) 实现不定长中文字符识别-附件资源
  • 不定长识别是将训练集图片的放缩feed到神经网络中的尺寸应用到测试中,test.py已经标注!) (如果有帮助到你,可以在Github给我个star!) (下一篇:斯坦福机器学习课程资料以及算法实现...

      扫描下方二维码添加微信拉入CV交流群

                   

    简单单人跟踪:https://github.com/Sierkinhane/human_tracker(基于目标检测与特征映射算法)

    演示视频:https://www.bilibili.com/video/av44360925

    斯坦福机器学习课程资料以及算法实现https://blog.csdn.net/Sierkinhane/article/details/82883161

    代码仓库:https://github.com/Sierkinhane/CS229-ML-Implements

     

         在六七月份参加了一个比赛,做的项目是提取图片中的文字信息,首先是接触了一些文本检测算法(如CTPN,East),后研究了文本识别算法(我认为较好的是CRNN)。代码实现是参考算法提出者的pytorch,python3版本的crnn实现。因为python版本的迭代,导致代码重使用比较难,其中涉及到ctc,python编码,中文数据集,如何将模型finetune到自己的应用场景上种种问题。实现的深度学习框架是pytorch,虽然TensorFlow也可以,但是比较多坑。其实是什么框架实现的都没关系,现在语法都是比较简单,看懂不难!

         因为自己已经踩了很多坑,也填好了这些坑,就将自己填好的项目贡献给大家!

         https://github.com/Sierkinhane/crnn_chinese_characters_rec 代码地址

         这次分享的是文本识别算法CRNN,具体的内容我就不涉及了,这篇文章主要是做算法代码的实现(参考原作者),建议大家研读算法一定要看作者发的Paper! CRNN论文地址:http://arxiv.org/abs/1507.05717作者是华中科技大学的老师

         先放一些效果图,利用360万的中文数据训练集,最后可以finetune到97.7%的验证准确率,训练好的模型在train_models文件夹

     

     

        第一、二张图片是最近修改的一个demo,第三、四张图是CTPN算法和CRNN的结合,可以将图片上的任何文字信息提取。因为CTPN要求的环境比较复杂,所以这次只放出CRNN的代码,因为CRNN实现环境比较简单。

        现在开始介绍代码:

                

    代码的实现必须是Linux环境(因为涉及到warp-ctc的安装,最好是Ubuntu16.04,能跳的坑我基本都填了)

    1. Warp-ctc安装

    首先得安装warp-ctc https://github.com/SeanNaren/Warp-ctc,这是pytorch版本的ctc实现(计算序列loss,具体看论文),安装方法按照作者的步骤即可,如果遇到问题可以私聊我。我是在Ubuntu16.04安装的,并没有太大问题,但是在17.04就遇到很多问题,所以最好用Ubuntu16.04作为代码实现环境。

    现在只需将pytorch更新至1.1.0,使用其自带的ctcloss即可。

    2. 测试

        安装好ctc后,直接运行终端输入 python3 test.py 试下效果,测试图片在test_images文件夹下。

    3. 训练

        正确的训练效果如图。

        训练之前首先制作数据集,因为360万的中文数据集制作成lmdb格式的数据有十几G,就没直接放到Github中。

        先下载360万中文数据集:https://pan.baidu.com/s/1ufYbnZAZ1q0AlK7yZ08cvQ

        对于数据集我想说明一下,在文字识别领域有比较多的识别场景,例如场景文本识别,比较正规的图片信息识别,这些不同的应用场景需要对应不同的数据集训练,这次我自己应用到的场景比较正规的字体识别,所以这个训练集不一定能够用到所有场景,但也确实提供了一个不错数据集资源!还有就是训练集最好是具有语义信息,如果只是将文字随机的组合生成图片作为训练集,模型收敛会更慢并且准确率受限!

       下图是部分训练集

          (这个数据是在Github中找到的,暂时没找到他的地址,很感谢作者的奉献!)

           数据集是随机选取定长的字数,经过模糊、倾斜、颜色变化等操作之后生成的,比较具有一般性,能很好地提升模型的Robust。

            下载好数据集之后如果解压出错,不完整,可以用好压进行修复。

            修改crnn_main_v2.py中的图片路径和标签路径,运行python crnn_main_v2.py即可

            接下来是制作lmdb格式的数据。

            图片与之对应的标签我链接:https://pan.baidu.com/s/1jfAKQVjD-SMJSffOwGhh8A 密码:u7bo,只需要将下载好的数据集放到lmdb文件中,根据情况修改to_lmdb.py中的文件名 运行该py程序就可以制作lmdb格式的数据!(需要用Python2来运行to_lmdb.py)

              制作好数据集之后将它放到lmdb_dataset文件夹中调出终端:

           python3 crnn_main.py --train_root 训练数据集路径 --val_root 验证集路径 --cuda (如果有cuda加速可选)

           大概流程就是这样了,最主要的还是自己看待自己琢磨!

           (不定长识别是将训练集图片的放缩feed到神经网络中的尺寸应用到测试中,test.py已经标注!)

           (如果有帮助到你,可以在Github给我个star!)

           (下一篇:斯坦福机器学习课程资料以及算法实现https://blog.csdn.net/Sierkinhane/article/details/82883161

     

     

      

    展开全文
  • CTPN DenseNet CTC based end-to-end Chinese OCR implemented using tensorflow and keras
  • 在六七月份参加了一个比赛,做的项目是提取图片中的文字信息,...因为python版本的迭代,导致代码重使用比较难,其中涉及到ctc,python编码,中文数据集,如何将模型finetune到自己的应用场景上种种问题。实现的深...

      在六七月份参加了一个比赛,做的项目是提取图片中的文字信息,首先是接触了一些文本检测算法(如CTPN,East),后研究了文本识别算法(我认为较好的是CRNN)。代码实现是参考算法提出者的pytorch,python3版本的crnn实现。因为python版本的迭代,导致代码重使用比较难,其中涉及到ctc,python编码,中文数据集,如何将模型finetune到自己的应用场景上种种问题。实现的深度学习框架是pytorch,虽然TensorFlow也可以,但是比较多坑。其实是什么框架实现的都没关系,现在语法都是比较简单,看懂不难!

         因为自己已经踩了很多坑,也填好了这些坑,就将自己填好的项目贡献给大家!

         https://github.com/Sierkinhane/crnn_chinese_characters_rec 代码地址

         这次分享的是文本识别算法CRNN,具体的内容我就不涉及了,这篇文章主要是做算法代码的实现(参考原作者),建议大家研读算法一定要看作者发的Paper! CRNN论文地址:http://arxiv.org/abs/1507.05717作者是华中科技大学的老师

         先放一些效果图,利用360万的中文数据训练集,最后可以finetune到97.7%的验证准确率,训练好的模型在train_models文件夹

     

     

        第一、二张图片是最近修改的一个demo,第三、四张图是CTPN算法和CRNN的结合,可以将图片上的任何文字信息提取。因为CTPN要求的环境比较复杂,所以这次只放出CRNN的代码,因为CRNN实现环境比较简单。

        现在开始介绍代码:

                

    代码的实现必须是Linux环境(因为涉及到warp-ctc的安装,最好是Ubuntu16.04,能跳的坑我基本都填了)

    1. Warp-ctc安装

    首先得安装warp-ctc https://github.com/SeanNaren/Warp-ctc,这是pytorch版本的ctc实现(计算序列loss,具体看论文),安装方法按照作者的步骤即可,如果遇到问题可以私聊我。我是在Ubuntu16.04安装的,并没有太大问题,但是在17.04就遇到很多问题,所以最好用Ubuntu16.04作为代码实现环境。

    2. 测试

        安装好ctc后,直接运行终端输入 python3 test.py 试下效果,测试图片在test_images文件夹下。

    3. 训练

        正确的训练效果如图。

        训练之前首先制作数据集,因为360万的中文数据集制作成lmdb格式的数据有十几G,就没直接放到Github中。

        先下载360万中文数据集:https://pan.baidu.com/s/1ufYbnZAZ1q0AlK7yZ08cvQ

        对于数据集我想说明一下,在文字识别领域有比较多的识别场景,例如场景文本识别,比较正规的图片信息识别,这些不同的应用场景需要对应不同的数据集训练,这次我自己应用到的场景比较正规的字体识别,所以这个训练集不一定能够用到所有场景,但也确实提供了一个不错数据集资源!还有就是训练集最好是具有语义信息,如果只是将文字随机的组合生成图片作为训练集,模型收敛会更慢并且准确率受限!

       下图是部分训练集

          (这个数据是在Github中找到的,暂时没找到他的地址,很感谢作者的奉献!)

           数据集是随机选取定长的字数,经过模糊、倾斜、颜色变化等操作之后生成的,比较具有一般性,能很好地提升模型的Robust。

            下载好数据集之后如果解压出错,不完整,可以用好压进行修复。

            接下来是制作lmdb格式的数据。

            图片与之对应的标签我链接:https://pan.baidu.com/s/1jfAKQVjD-SMJSffOwGhh8A 密码:u7bo,只需要将下载好的数据集放到lmdb文件中,根据情况修改to_lmdb.py中的文件名 运行该py程序就可以制作lmdb格式的数据!

              制作好数据集之后将它放到lmdb_dataset文件夹中调出终端:

           python3 crnn_main.py --train_root 训练数据集路径 --val_root 验证集路径 --cuda (如果有cuda加速可选)

           大概流程就是这样了,最主要的还是自己看待自己琢磨!

           (不定长识别是将训练集图片的放缩feed到神经网络中的尺寸应用到测试中,test.py已经标注!)

           (如果有帮助到你,可以在Github给我个star!)

    --------------------- 本文来自 Sierkinhane 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/Sierkinhane/article/details/82857572?utm_source=copy 

    转载于:https://www.cnblogs.com/sierkinhane/p/9715582.html

    展开全文
  • STM32串口中断+DMA接收不定长字符

    千次阅读 2019-10-14 21:09:45
    STM32串口中断+DMA接收不定长字符串 硬件平台:stm32f103rct6(大容量产品) 软件环境:KEIL MDK+stm32f1固件库 串口:USART1 ----------------------------------------------------------------------------- 看了...

    STM32串口中断+DMA接收不定长字符串

    硬件平台:stm32f103rct6(大容量产品)
    软件环境:KEIL MDK+stm32f1固件库
    串口:USART1
    -----------------------------------------------------------------------------
    看了网上很多讲串口中断+DMA接收不定长字符串的例子,要么大家抄来抄去不加验证,要么没有源代码,要么对空闲中断解释不清楚。 今天有空来整理一下,下面的代码经过本人验证。
    接收定长字符串简单,直接在中断里将字符放到数组里即可。
    但是接收不定长字符串就比较麻烦,因为你不知道什么时候一帧数据接收完了。通常有两种方法来接收不定长字符串:超时判断串口空闲中断。今天我们先介绍第二种方法。
    首先明确一个概念,什么是STM32的空闲中断,下面是stm32数据手册1对空闲中断的定义:

    空闲符号被视为完全由’1’组成的一个完整的数据帧,后面跟着包含了数据的下一帧的开始位(‘1’的位数也包括了停止位的位数)。

    也就是全部由1组成的一帧数据是空闲帧,包括起始位停止位都是1。当我们用串口助手发送完一串数据后,TXD引脚默认为高电平,此时我们可以认为发送完这一串的数据的最后一个字节后,紧跟着一个空闲中断。图1是手册里面的解释。
    图1.空闲中断示意图
    问题解决思路:用DMA1的通道5接收串口数据,当一帧数据发送完,进入32的串口中断,在串口中断里面计算本次DMA传输接收了多少字符,同时置位标志位,在main()函数中查询标志位来处理数据,别忘了处理完之后将标志位清零。
    下面是代码:
    串口1初始化:

    void usart1_config(u32 baud)
    {
    GPIO_InitTypeDef io;
    USART_InitTypeDef temp;
    //RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
    
    io.GPIO_Pin = GPIO_Pin_9;
    io.GPIO_Mode = GPIO_Mode_AF_PP;//txd
    io.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &io);
    
    io.GPIO_Pin = GPIO_Pin_10;
    io.GPIO_Mode = GPIO_Mode_IN_FLOATING;//rxd
    io.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &io);
    
    
    temp.USART_BaudRate = baud;
    temp.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    temp.USART_Mode = USART_Mode_Rx|USART_Mode_Tx;
    temp.USART_Parity = USART_Parity_No;
    temp.USART_StopBits = USART_StopBits_1;
    temp.USART_WordLength = USART_WordLength_8b;
    USART_Init(USART1, &temp);
    
    #if(USART1_RX_INT_EN)//这个宏为1
    {
    	NVIC_InitTypeDef NVIC_InitStruct;
    	
    	NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
    	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStruct);
    
    	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//使能串口1的空闲中断,注意这里并没有使能接收中断!
    }
    #endif
    USART_Cmd(USART1, ENABLE);
    }
    

    DMA初始化

    void usart1_DMA_config(DMA_Channel_TypeDef* DMAy_Channelx,u32 memAddr,u32 periphAddr,u8 bufSize)
    {
    DMA_InitTypeDef  DMA_InitStructure;
    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);//DMA1???? 
    /* 初始化DMA */
    DMA_InitStructure.DMA_PeripheralBaseAddr = periphAddr;//	DMA外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = memAddr;//			DMA内存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//		·传输方向从外设到内存
    DMA_InitStructure.DMA_BufferSize = bufSize;					//传输数据大小
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//传输一次后外设地址不增加
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//传输一次后内存地址增加
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//按照字节传送
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//按照字节传送
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//正常模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//优先级中等,一个DMA通道工作的话可以不设置
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //关闭内存到内存
    DMA_Init(DMAy_Channelx, &DMA_InitStructure);//初始化
    
    #if(USART1_RX_DMA_INT_EN )//这个宏定义没开
    {
    	NVIC_InitTypeDef NVIC_InitStruct;
    	
    	NVIC_InitStruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;//USART1_RX-channel5
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
    	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    	NVIC_Init(&NVIC_InitStruct);
    	DMA_ITConfig(DMAy_Channelx,DMA_IT_TC,ENABLE);
    }
    #endif
    }
    

    DMA的使能
    包括两部分,DMA使能与串口的接收请求使能。

    void usart1_DMA_enable(DMA_Channel_TypeDef* DMAy_Channelx,u8 bufSize)
    {
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);//DMA接收请求使能
    DMA_Cmd(DMAy_Channelx, DISABLE);//先关闭才能写传送的数据
    DMA_SetCurrDataCounter(DMAy_Channelx, bufSize);//写入传送数量,如果此值为0DMA开不了
    DMA_Cmd(DMAy_Channelx, ENABLE);
    }
    

    串口中断函数

    void USART1_IRQHandler(void)
    {
        u16 clear;
    	if(USART_GetFlagStatus(USART1, USART_FLAG_IDLE))
    	{
    		clear = USART_ReceiveData(USART1);//通过先读状态寄存器再读数据寄存器将IDLE清除
    		usart1IdleItFlag = 1;//接收完成标志位置1
    		bufRemainByte = 50 - DMA_GetCurrDataCounter(DMA1_Channel5);//计算本次传送的字节数。总数以50为例子
    		PBout(11) = ~PBout(11);//配合led观察接收完毕
    	}
    }
    

    上面需要注意,清除IDLE标志位的次序,根据数据手册的说明:

    IDLE:监测到总线空闲 (IDLE line detected)
    当检测到总线空闲时,该位被硬件置位。如果USART_CR1中的IDLEIE为’1’,则产生中断。由
    软件序列清除该位(先读USART_SR,然后读USART_DR)。

    注意比对上面代码。

    在main()函数里面查询标志位处理数据

    int main()
    {
    ST_USART1_RCV_BUF rxbuf={{0},0,0,0};
    extern u8 usart1IdleItFlag;
    extern u8 bufRemainByte; 
    
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(2);
    LED_Init();
    usart1_config(9600);
    usart1_DMA_config(DMA1_Channel5,(u32)rxbuf.buf,(u32)&USART1->DR,50);
    usart1_DMA_enable(DMA1_Channel5,50);
    while(1)
    {
    	if(usart1IdleItFlag)
    	{
    		usart1_send_string(rxbuf.buf,bufRemainByte);
    		usart1_DMA_enable(DMA1_Channel5,50);//ʹÄÜÏÂÒ»´ÎDMA
    		usart1IdleItFlag = 0;//一定不要忘记将标志位清零。
    	}
    }
    

    以上代码别写结束,下面是测试:
    下面是测试图
    图2.串口测试
    如上图,发送了8次“12345678”,进入空闲中断8次,一共接收到64个字符。测试完毕。

    第一次发帖子,不足之处请提出意见。
    详细信息可以加qq交流:1130139818



    1. STM32F1xx中文参考手册.pdf ↩︎

    展开全文
  • 不定长度字符 一开始,字符串编码这件事很简单。ASCII码是一组从0到127的整数,因为128 = 2 ^ 7,因此如果把它存在八个字节中,还能多余一位。所以字符串中的每一个字符可以随机检索[1]。 但是对于非英语国家的人来...

    本文系阅读阅读原章节后总结概括得出。由于需要我进行一定的概括提炼,如有不当之处欢迎读者斧正。如果你对内容有任何疑问,欢迎共同交流讨论。

    不定长度字符

    一开始,字符串编码这件事很简单。ASCII码是一组从0到127的整数,因为128 = 2 ^ 7,因此如果把它存在八个字节中,还能多余一位。所以字符串中的每一个字符可以随机检索[1]

    但是对于非英语国家的人来说,他们需要的符号远不是128个ASCII码能表示的(比如汉字)。ISO/IEC 8859标准利用了空余的第八个位,拓展了很多符号,但依然不够。当我们把这八个bit位全部用上,但还是有些符号无法表示的时候,我们可以选择继续增加bit位数,比如用16个位来存储字符,或者可以让每个字符占用的bit位是可变的。Unicode最初使用了2字节的固定长度,这意味着它可以存储2 ^ 16 = 65536个字符,不过目前看来依然不够,但如果增加到4字节,在通常情况下效率又太低。

    在进一步学习之前,有必要理清楚Unicode编码中的几个概念:

    • 字符:字符是抽象的最小文本单位,它没有固定的形状(比如A都是字符),字符没有值。

    • 字符集;字符集是字符的集合。比如所有汉字构成汉字字符集,还有英文字符集、日语字符集等等。

    • 编码字符集:这是一种特殊的字符集。它为每个字符分配一个惟一的数字。Unicode标准的核心是Unicode编码字符集,比如字符A会分配一个数字0041,Unicode中的数字总是使用16进制。

    • 代码点:英文是Code Point,它表示可用于编码字符集的数字。代码点U+0041对应的字符是A。编码字符集会定义代码点的取值范围,但是在这个范围内,并非每个数字(代码点)都有对应的字符。

    • 编码方式:编码方式表示了从一个代码点到一个或多个代码单元映射方式。常见的编码方式有UTF-32、UTF-16、UTF-8。

    • 代码单元:代码单元是每一种编码方式下的最基本单元。UTF-32表示代码单元是32位,因为16进制的00000041恰好也是32位,所以UTF-32编码方式非常简单:一个代码点映射到一个代码单元,且两者值相同。UTF-16下,一个代码单元是16位,但这不表示00000041一定映射成00000041。UTF-16编码方式有自己的映射规则,UTF-8也是同理。

    以字母A为例,A是英文字符集中的一个字符,它的代码点是00000041,在UTF-32编码规则下的代码单元是00000041,UTF-16下的代码单元是0041,UTF-8下的代码单元是41

    ?是一个字符,它的代码点是U+10400,UTF-32下的代码单元是00010400,UTF-16下的代码单元是D801DC00,UTF-8下的代码单元有四个:F0909080

    目前Unicode使用了可变宽度格式,这体现在两个方面:

    1. 代码点映射到的代码单元数量可变。在之前的例子中可以发现一个代码点在UTF-8下可以映射成1~4个代码单元。
    2. 组成字符的代码点数量可变。可能存在多个代码点组合成一个字符的情况,这一点我们待会儿会看到具体的例子。

    Unicode标量是另外一些代码单元,它们可以当做代码点来用(除了UFT-16的代理对以外)。在Swift中,标量用字符串字面量"\u{xxxx}"表示,这里的xxxx是一个16进制的数字。

    之前我们说过,组成字符的代码点数量可变。也就是说用户在屏幕上看到的一个字符,可能是由多个代码点组成的。大多数处理字符串的代码一定程度上都没有注意到Unicode可变宽度的特性,这可能会导致一些bug。Swift在字符串时,花费了巨大的努力,尽可能正确的使用了Unicode。至少会在有错误时让开发者知道。这也付出了一定的代价,String类型并不是一个集合,而是提供了多种不同的视角来观察字符串,你可以把字符串当做字符(Character)的集合,也可以当做UTF-8或UTF-16编码下的代码单元的集合,或是Unicode标量的集合。Character和另外几个视图的区别在于,它可以把若干个代码点组合成一个“字形集群(Grapheme Cluster)

    出了UTF-16以外的所有视图都无法通过下标随机访问,不同的视图在处理大量文本处理时有快有慢,在本章我们会探索其背后的原因。我们还会了解一些处理文本和提高性能的技术。

    字形集群和规范等价

    为了展示Swift和NSString处理Unicode字符的区别,我们来分析一下打印字符é的方法。作为一个单个字符,它的Unicode代码点是U+00E9。但它也可以表示为字母e后面加一个́(代码点U+0301)。无论选择那种表示方法,最终显示的结果都是é,对于用户来说不仅字符串相同,长度也相同,都是1。这就是Unicode中“规范等价(Canonically equivalent)”。

    我们在Swift中举一个具体的例子,这两个字符串的显示效果完全相同:

    let single = "Pok\u{00E9}mon"
    let double = "Pok\u{0065}\u{0301}mon"
    
    print(single, double)
    // 输出结果是“Pokémon Pokémon”
    复制代码

    还可以证明一下他们的字符串变量时相等的,字符数量也相等:

    print(single == double)    // 输出结果:true
    print(single.characters.count == double.characters.count)    // 输出结果:true
    复制代码

    不过,如果切换成UTF-16视图,就可以看出两者的区别了:

    print(single.utf16.count)	// 输出结果为7
    print(double.utf16.count)	// 输出结果为8
    复制代码

    如果使用NSString,不仅字符数量不同,字符串本身也不相同:

    let nssingle = NSString(characters: [0x0065, 0x0031], length: 2)
    let nsdouble = NSString(characters: [0x00E9], length: 1)
    
    print(nssingle == nsdouble)		//输出结果是:false
    print(nssingle.isEqualToString(nsdouble as String))     //输出结果是:false
    复制代码

    其中等号运算符比较的是两个NSObject类型的对象,它的定义是:

    func ==(lhs: NSObject, rhs: NSObject) -> Bool {
    return lhs.isEqual(rhs)
    }
    复制代码

    这是因为在NSString的比较方法中,只考虑字面量是否相等,不会考虑多个字符的组合结果是否是“规范等价”的。如果你真的想进行规范比较,那么需要使用NSStringcompare方法。啥,你不知道这个方法?不好意思,那你就等着以后的iOS开发和数据库开发中不停地报错吧。

    直接比较代码单元的优点在于速度非常快,比用characters快很多。比如:

    print(single.utf16.elementsEqual(double.utf16))     //输出结果是:false
    复制代码

    不仅仅是两个字符可以拼接组合成一个,更多的字符也可以拼接。比如约鲁巴语中有一个字符:ọ̀,它中间是字母o,上面是一个类似于汉语中第四声调的字符:"`",下面则是一个点:"."。它有四种表示方法:

    1. 字母o和其中一个符号拼接后的符号,和另一个符号拼接。这有两种方法
    2. 三个字符分别拼接,其中o位于开头,后面两个字符的顺序可以对调。这又是两种方法。

    我们用代码表示:

    // U+6F是字母o,U+300是第四声,U+323是"."
    let chars: [Character] = [
    "\u{1ECD}\u{300}",  // U+1ECD是U+6F和U+323的拼接结果,等价于:(o + .) + 第四声
    "\u{F2}\u{323}",  // U+F2是U+6F和U+300的拼接结果,等价于:(o + 第四声) + .
    "\u{6F}\u{323}\u{300}",  // 等价于:o + . + 第四声
    "\u{6F}\u{300}\u{323}",  // 等价于:o + 第四声 + .
    ]
    
    for char in chars {
    print(char)
    }
    
    /** 打印结果:
    
    ọ̀
    ọ̀
    ọ̀
    ọ̀
    
    */
    复制代码

    事实上,这种声调符是可以无限添加的,不过长度依然是1:

    let many = "\u{1ECD}\u{300}\u{300}\u{300}\u{300}"
    print(many.characters.count)   // 输出结果:1
    print(many.utf8.count)	// 输出结果:11,U+1ECD在UTF-8下由3个代码单元组成,U+300由2个组成,11 = 3 + 2 * 4
    print(many)
    
    /* 字符串输出结果:
    
    ọ̀̀̀̀
    
    */
    复制代码

    Emoji

    Emoji表情不是很重要,但是很好玩。搞懂下面这个问题有助于帮助我们理解Unicode标量的拼接:

    let emoji1 = "????????????"
    let emoji2 = "???"
    
    print(emoji1.characters.count)
    print(emoji2.characters.count)
    复制代码

    如果你认为打印结果分别是6和3,那么你就上当了。答案是1和3。回想一下之前ọ̀这个字符,他有四种组成方法,但是细心的读者可能会问,为什么"\u{300}\u{6F}\u{323}"这种写法(也就是第四声+o+.)不行?

    这是因为在Unicode中,有些字符称为基字符(base)。只有这种字符是可以向后拓展的,我们之前所说的字形集群的定义是:“一个基字符,加上后面0或多个字符”。

    所以,输出结果是1而不是6的原因在于,Unicode规范中国旗是一个基字符,6个国旗拼接在一起会被认为是一个字形集群,也就是依然是一个字符。而?并不是基字符,所以可以被正确识别为3个字符。

    译者注

    [1]:考虑字符串hello,只要知道字符o是第5个字符,因为每个字符的长度固定,都是8个bit位,所以立刻可以到第(5 - 1) * 8 = 32个bit位去查找字符o。这就是原文中random access的含义。如果每个字节长度不定,则需要从头开始遍历。

    展开全文
  • MySQL中不定长字符类型VARCHAR(255)

    千次阅读 2019-10-09 15:59:15
    数据库MySQL中的 varchar MySQL在4.1版本以前: VARCHAR是以字节为单位...MySQL在4.1版本及以后:VARCHAR是以字符为单位来进行存储,假设输入仍然为常用汉字,则VARCHAR(255)可以存放255个汉字。 另外,MySQL对U...
  • str1 = MessageBox.Show("'t0030提示A006M表没有记录'" + acReg + "'!", "错误提示"); str2 = "112新建记事本anc抢分是;什么东西&w'英文";...希望方法简洁、高效,因为字符串可能会比较大!多谢达人
  • 输入不定长且未知长度的字符串,使用动态字符串输入。 循环交换两列动态生成的字符串,并在每次交换后加长并释放上次申请的内存
  • 犹豫代码比较,讲起来也比较麻烦,所以长话短说,讲一讲使用方法,我先把源码粘贴出来,比较,可以直接点上边的锚跳过这段: import cv2 import random import os import pandas as pd import numpy as np ...
  • java String中文字符串分割成一定长度的字符串数组 /** * 几个字一组 变量控制 大于零有意义 */ int num = 6; /** * 待操作的字符串 */ String str = "一二三四五六七一二三四五六七一二三四五六七一二三...
  • 遍历字典,取出KEY和VALUE,对VALUE中如果有中文字符的话,进行替换 (2) (a)NSString *encodeInput = (NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes( kCFAllocatorDefault, ...
  • URL中文字符

    2014-04-10 10:31:54
    将URL中的中文字符转码,URL中不定参数
  • 网上搜索的答案大多不沾边,唯一有一个是说64位的oracle和32位的客户端可能导致此问题。我这边检查了下,都是64位的,不是这里的...对两个页面的代码调试中输出的存储过程字符串输出完全了。没有再出现被截断的情况
  • 在使用GirdView编辑更新时,如果输入的字符串字节长度超过50了会出现“将截断字符串或二进制数据” 的错误。 但是又不能简单的限制文本框的长度,因为中文占2个字节。所以串的字节长度是不定的。这个时候就要根据串...
  • Crnn_chinese_characters 中文字符识别

    万次阅读 2019-01-15 19:21:31
    Crnn_chinese_characters...文字识别(OCR)CRNN(基于pytorch、python3) 实现不定长中文字符识别 一、实验环境 没网、没权限、centos 64 二、实验步骤 1、离线安装 anaconda anaconda清华镜像源 先下载anaconda3-4....
  • 汉字字符的统计

    2015-06-15 13:34:02
    以GB2312为标准,说一下我对汉字字符统计的一点认识。 ASCII码:一个字符占一个字节,以0开头; 中文字符:一个汉字占两个字节,以1开头。 ASCII码的161-255这中间94个字符很少用,所以用来作为中文字符的编码。...
  • 中文在不同编码中占用的字节数是不同的,GBK编码中,一个汉字占两个字节...如果我们按照字节数来截取带中文字符串,就有可能截取了半个或者三分之一个汉字,导致乱码的出现。 [code="java"] package c...
  • 一、步骤:1、计算长度 2、根据长度截取第一部分字符串 3、截取中间部分字符串 4、截取最后一部分字符串或者去掉前两部分字符串 ... // 计算字符串长度,无论汉字还是英文字符全部为1 $length = mb_s
  • 分享方法:按字符串真实长度(字节长度)进行截取
  • 字符与Unicode (c语言 汉语字符串长度) 在C语言中,我们使用char来定义字符,占用一个字节,最多只能表示128个字符,也就是ASCII码中的字符。计算机起源于美国,char 可以表示所有的英文字符,在以英语为母语的...
  • 利用正则表达式计算含有中文字符串长度
  • 由于经常需要登录某系统进行测试,但系统里有...其中速度上ocr和机器学习去识别验证码的速度是最快的,但ocr需要验证码的字符很规范才能进行识别,否则也是识别不了的。而我目前想识别类似这样的验证码,那么ocr的...
  • tf22: ocr识别——不定长数字串识别

    千次阅读 2017-11-20 15:07:18
    你要的答案或许都在这里:小鹏的博客目录 OCR工程git:https://github.com/MachineLP/OCR_repo ... 上一篇:身份证识别——生成身份证号和汉字 代码如下: #!/usr/bin/env python2 # -*- coding: utf...
  • 上述方法目前已经不再流行,面临的显而易见的问题就是容易造成将当个字符切开,导致后续分别出错,而且汉字处理识别成本较高,当下更流行的是基于深度学习的端到端的文字识别,我们不需要显示的对汉字进行切割,而是...
  • 为了测试程序对多语言字符的支持情况,我找来一段中文和北欧的文字,希望把这些文字上传到elasticsearch,并能正确显示。 首先测试了北欧文字,一切OK。 但是中文复制到 VNC 客户端(Linux)后却是问号,因为Linux...
  • 但是实际应用场景中,常常需要面对无法确定字串长度的情况,这时候除了需要对识别字符模型参数进行训练外,还需要对字符划分模型进行训练,本文实现了上文提到的方法2,使用LSTM+CTC识别不定长的数字串。 环境依赖 ...
  • barcode4j EAN128不定长msg分隔符条形码CODE128 代码实现EAN128代码实现如何改变文本的样式EAN128常见错误-不定长数据报错关于阅读jar包内容其他参考 条形码 首先我们需要了解条形码是怎样的。 CODE128: Code128码可...
  • ☯,首先,这并不是图片,这是一个unicode字符,Yin Yang,即阴阳符,码点为U+262F。如果你的浏览器无法显示,可以查看这个链接http://www.fileformat.info/info/unicode/char/262f/index.htm。这与我们要讨论的主题...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 13,909
精华内容 5,563
关键字:

不定长中文字符