中英文字符大小和文本边界问题

中英文字符大小和文本边界问题

也不知道是楼主的问题还是CSDN的问题,楼主最近的博客在贴代码的时候出现了问题,明明把代码贴进去了,还是没有代码的效果,好吧.先这样吧,过几天再看看到底是啥情况.

ASCII,UTF-8,Unicode编码下的中英文字符大小

当对字符串进行发送和接受时,编码方式很关键,服务端与客户单显然要采用相同的编码方式才行,否则一方的到的就是乱码.演示一下,看一下常见的编码格式下中英文字符的写法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ShowCode
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] strArray = { "b","abcd","乙","甲乙丙丁"};

            byte[] buffer;
            string mode, back;
            foreach (string str in strArray)
            {
                for (int i = 0; i < 3; i++)
                {
                    if (i==0)
                    {
                        buffer = Encoding.ASCII.GetBytes(str);
                        back = Encoding.ASCII.GetString(buffer,0,buffer.Length);
                        mode="ASCII";
                    }
                    else if (i==1)
                    {
                        buffer = Encoding.UTF8.GetBytes(str);
                        back = Encoding.UTF8.GetString(buffer,0,buffer.Length);
                        mode = "UTF-8";
                    }
                    else
                    {
                        buffer = Encoding.Unicode.GetBytes(str);
                        back = Encoding.Unicode.GetString(buffer,0,buffer.Length);
                        mode = "Unicode";
                    }
                    Console.WriteLine("Mode: {0},String: {1},Buffer.Length:{2}",mode,str,buffer.Length);

                    Console.Write("Buffer: ");
                    for (int j = 0; j < buffer.Length; j++)
                    {
                        Console.Write(buffer[j]+" ");
                    }
                    Console.WriteLine("\nRetrived: {0}\n",back);
                }
            }

        }
    }
}

关于代码的输出,你可以自己看一下:

Mode: ASCII,String: b,Buffer.Length:1
Buffer: 98
Retrived: b

Mode: UTF-8,String: b,Buffer.Length:1
Buffer: 98
Retrived: b

Mode: Unicode,String: b,Buffer.Length:2
Buffer: 98 0
Retrived: b

Mode: ASCII,String: abcd,Buffer.Length:4
Buffer: 97 98 99 100
Retrived: abcd

Mode: UTF-8,String: abcd,Buffer.Length:4
Buffer: 97 98 99 100
Retrived: abcd

Mode: Unicode,String: abcd,Buffer.Length:8
Buffer: 97 0 98 0 99 0 100 0
Retrived: abcd

Mode: ASCII,String: 乙,Buffer.Length:1
Buffer: 63
Retrived: ?

Mode: UTF-8,String: 乙,Buffer.Length:3
Buffer: 228 185 153
Retrived: 乙

Mode: Unicode,String: 乙,Buffer.Length:2
Buffer: 89 78
Retrived: 乙

Mode: ASCII,String: 甲乙丙丁,Buffer.Length:4
Buffer: 63 63 63 63
Retrived: ????

Mode: UTF-8,String: 甲乙丙丁,Buffer.Length:12
Buffer: 231 148 178 228 185 153 228 184 153 228 184 129
Retrived: 甲乙丙丁

Mode: Unicode,String: 甲乙丙丁,Buffer.Length:8
Buffer: 50 117 89 78 25 78 1 78
Retrived: 甲乙丙丁

这样可以得出几个结论:

1.ASCII不能保存中文

2.UTF-8和Unicode都是边长编码.在对ASCII字符编码时,UTF-8更省空间,只占一个字节,与ASCII编码方式和长度相同;Unicode在对ASCII字符编码时,占用2个字节,且第二个字节补零.

3.UTF8在对中文编码是需要占用3字节;Unicode对中文编码只需要2个字节.

不知道你注意到没有,咱们前面客户端-服务端采用的编码方式都是Unicode,这样才能保证两边的字符都能正确显示.

文本边界问题

除了字符编码问题之外,还存在一个问题:当客户端分两次向流中写入字符串时,我们主观上将这两次写入视为两次数据发送;然而服务器有可能将这两次合起来作为一条数据发送,这在两个请求间隔时间比较短的情况下尤其如此.同样,也有可能客户端只进行了一次数据发送,但是服务端分成了两个请求进行处理.

假设在客户端发送两条”Welcome to SDUT.COM!”,则数据到达服务端时可能出现三种情况.

 

第一种是理想的情况,此时两条消息被视为两个独立请求由服务端完整接受.

 

第二种是一条消息被当做两条消息接受了.

 

第三种是两条消息合并为一条了.

出现这种情况的原因是,当通过NetworkStream写入数据时,数据并没有立即发送远程主机,而是保存在了TCP缓存(TCP
Buffer)中,经过一段时间之后才发送:

对于传输二进制文件来说,这不是事,但是对于文本文件来说,就需要确定两次发送文本的边界,否则就可能给对方带来困惑.先来模拟一下这种情况,对客户端进行修改,不通过用户输入,而是通过一个for循环连续发送三条数据过去,这样发送数据的间隔更短.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("CLient is running...");
            TcpClient client;
            const int BufferSize = 8192;

            try
            {
                client = new TcpClient();
                //与服务器建立连接
                client.Connect(IPAddress.Parse("192.168.3.19"), 9322);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return;
            }
            //打印连接到的服务端信息
            Console.WriteLine("Server Connected! Local: {0} --> Server: {1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint);

            NetworkStream streamToServer = client.GetStream();

            string msg;
            Console.Write("Sent:");
            msg = "Welcome to SDUT.COM!";
            for (int i = 0; i < 3; i++)
            {
                byte[] buffer = Encoding.Unicode.GetBytes(msg);//获得缓存
                try
                {
                    streamToServer.Write(buffer, 0, buffer.Length);
                    Console.WriteLine("Sent: {0}", msg);
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    break;
                }
            }
            streamToServer.Dispose();
            client.Close();
        }
    }
}

运行服务器,再运行客户端,可能会出现意想不到的情况,为啥呢?大家回想一下HTTP协议,在实际的请求和应答之前包含了HTTP头,其中是一些与请求相关的信息,比如浏览器信息,Cookie信息,请求方式信息等.我们也可以订立自己的协议来解决这个问题,比如,对于上面的情况,我们就可以这么定义一个协议:

[length=XXX]

其中XXX是实际发送的字符串长度(注意不是字节数组buffer的长度),因此对于上面的请求,实际发送的数据为”[length=20]Welcome
to SDUT.COM!”.而服务端接受字符串之后,首先读取这个”元数据”的内容,然后再根据”元数据”内容来读取实际的数据.”元数据”内容可能有下面这样两种情况:

1.”[” ”]”中括号是完整的,可以读取到length的字节数.接着根据这个数值与后面的字符串长度相比,如果相等,这说明发送了一条完整数据;多了说明接受的字节数多了,取出合适的长度,将剩余的进行缓存;少了说明接收的不够,将受到的字节进行一个缓存,等待下次请求,然后将两条信息合并.

2.“[” “]”中括号本身就不完整,此时读不到length的值,因为中括号里的内容被截断了,所以将读到的数据进行缓存,等待读取下次发送来的数据,然后将两次数据合并之后再按上面的方式进行处理.

看一个案例:

        private string[] GetActualString(string input, List<string>outputList)
        {
            if (outputList==null)
            {
                outputList = new List<string>();
            }
            if (!string.IsNullOrEmpty(temp))
            {
                input = temp + input;
            }

            string output = "";
            string pattrern = @"(?<=^\[length=])(\d+)(?=\])";

            int length;

            if (Regex.IsMatch(input, pattrern))
            {
                Match m = Regex.Match(input, pattrern);

                //获取消息字符串实际应用的长度
                length = Convert.ToInt32(m.Groups[0].Value);

                //获取需要进行截取的位置
                int startIndex = input.IndexOf(']')+1;

                //获取从此位置开始后所有字符的长度
                output = input.Substring(startIndex);

                if (output.Length==length)
                {
                    //如果output的长度与消息字符串的应用长度相等
                    //说明刚好是完整的一条消息
                    outputList.Add(output);
                    temp = "";
                }
                else if (output.Length<length)
                {
                    //如果之后的长度小于应用的长度
                    //说明没有发完整,则应将整条信息,包括元数据,全部缓存
                    //与下一条数据合并起来再进行处理
                    temp = input;
                    //此时程序应该退出,因为需要等待下一条数据到来才能继续处理
                }
                else if (output.Length>length)
                {
                    /*
                    如果之后的长度大于应用的长度
                    说明消息发完整了,但是有多余的数据
                    多余的数据可能是截断消息,也可能是多条完整消息.
                    */
                    //截取字符串
                    output = output.Substring(0,length);
                    outputList.Add(output);
                    temp = "";

                    //缩短intput的长度
                    input = input.Substring(startIndex+length);

                    //递归调用
                    GetActualString(input,outputList);
                }
            }
            else//说明"[","]"就不完整
            {
                temp = input;
            }
            return outputList.ToArray();
        }

这个方法接受一个满足协议格式要求的输入字符串,然后返回一个数组,这是因为如果出现多次请求合并成一个发送过来的数据,那么就将他们全部返回.随后为了简单起见,在这个类中添加了一个静态的Test()方法和PrintOutput()帮主方法,进行了一个简单的测试,注意这里直接输入了length=13,这是提前计算好的.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ShowCode
{
    public class RequestHandler
    {
        private string temp = string.Empty;
        public string[] GetActualString(string input)
        {
            return GetActualString(input,null);
        }

        private string[] GetActualString(string input, List<string>outputList)
        {
            if (outputList==null)
            {
                outputList = new List<string>();
            }
            if (!string.IsNullOrEmpty(temp))
            {
                input = temp + input;
            }

            string output = "";
            string pattrern = @"(?<=^\[length=)(\d+)(?=\])";

            int length;

            if (Regex.IsMatch(input, pattrern))
            {
                Match m = Regex.Match(input, pattrern);

                //获取消息字符串实际应用的长度
                length = Convert.ToInt32(m.Groups[0].Value);

                //获取需要进行截取的位置
                int startIndex = input.IndexOf(']')+1;

                //获取从此位置开始后所有字符的长度
                output = input.Substring(startIndex);

                if (output.Length==length)
                {
                    //如果output的长度与消息字符串的应用长度相等
                    //说明刚好是完整的一条消息
                    outputList.Add(output);
                    temp = "";
                }
                else if (output.Length<length)
                {
                    //如果之后的长度小于应用的长度
                    //说明没有发完整,则应将整条信息,包括元数据,全部缓存
                    //与下一条数据合并起来再进行处理
                    temp = input;
                    //此时程序应该退出,因为需要等待下一条数据到来才能继续处理
                }
                else if (output.Length>length)
                {
                    /*
                    如果之后的长度大于应用的长度
                    说明消息发完整了,但是有多余的数据
                    多余的数据可能是截断消息,也可能是多条完整消息.
                    */
                    //截取字符串
                    output = output.Substring(0,length);
                    outputList.Add(output);
                    temp = "";

                    //缩短intput的长度
                    input = input.Substring(startIndex+length);

                    //递归调用
                    GetActualString(input,outputList);
                }
            }
            else//说明"[","]"就不完整
            {
                temp = input;
            }
            return outputList.ToArray();
        }

        public static void Test()
        {
            RequestHandler handler = new RequestHandler();
            string input;

            //第一种情况:一条消息完整发送
            input = "[length=13]欢迎各位来到山东理工大学!";
            handler.PrintOutput(input);

            //第二种情况:两条完整消息一次发送
            input = "欢迎各位来到山东理工大学!";
            input = string.Format("[length=13]{0}[length=13]{0}",input);
            handler.PrintOutput(input);

            //第三种情况测试A:两条消息不完整发送
            input = "[length=13]欢迎各位来到山东理工大学![length=13]欢迎各位";
            handler.PrintOutput(input);
            input = "来到山东理工大学!";
            handler.PrintOutput(input);

            //第三种情况测试B:两条消息不完整发送
            input = "[length=13]欢迎各位来到山东";
            handler.PrintOutput(input);

            input = "理工大学![length=13]欢迎各位来到山东理工大学!";
            handler.PrintOutput(input);

            //第四种情况:元数据不完整
            input = "[leng";
            handler.PrintOutput(input);//不会有输出
            input = "th=13]欢迎各位来到山东理工大学!";
            handler.PrintOutput(input);

        }

        private void PrintOutput(string input)
        {
            Console.WriteLine("输入: "+input);
            string[] outputArray = GetActualString(input);
            foreach (string output in outputArray)
            {
                Console.WriteLine("输出: "+output);
            }
            Console.WriteLine();
        }
    }
}

测试代码如下:

        static void Main(string[] args)
        {
            RequestHandler.Test();
        }

运行程序:

输入: [length=13]欢迎各位来到山东理工大学!
输出: 欢迎各位来到山东理工大学!

输入: [length=13]欢迎各位来到山东理工大学![length=13]欢迎各位来到山东理工大学!
输出: 欢迎各位来到山东理工大学!
输出: 欢迎各位来到山东理工大学!

输入: [length=13]欢迎各位来到山东理工大学![length=13]欢迎各位
输出: 欢迎各位来到山东理工大学!

输入: 来到山东理工大学!
输出: 欢迎各位来到山东理工大学!

输入: [length=13]欢迎各位来到山东

输入: 理工大学![length=13]欢迎各位来到山东理工大学!
输出: 欢迎各位来到山东理工大学!
输出: 欢迎各位来到山东理工大学!

输入: [leng

输入: th=13]欢迎各位来到山东理工大学!
输出: 欢迎各位来到山东理工大学!

完美!!!接下来就让我们看看使用异步方式实现对多客户端多请求的处理.

时间: 2024-11-03 22:09:44

中英文字符大小和文本边界问题的相关文章

ASCII,Utf8,Unicode编码下的中英文字符大小

一,测试Demo namespace 不同编码下的中英文字符大小 { class Program { static void Main(string[] args) { ShowCode(); } private static void ShowCode() { string[] strArray = { "b","abc","乙","甲乙丙丁"}; byte[] buffer; string mode, back; fore

截取文本,区分中英文字符,中文算两个长度,英文算一个长度

/// <summary>    /// 截取文本,区分中英文字符,中文算两个长度,英文算一个长度   /// </summary>   /// <param name="str">待截取的字符串</param>   /// <param name="length">需计算长度的字符串</param>   /// <returns>string</returns>   p

Android EditText中字符大小与光标位置

最近的工作需要自己写一个数字键盘用于数字的输入,采用的方法是自定义一个  Dialog,然后将数字显示在EditText中.在处理过程中遇到遇到一个问题,在EditText的点击事件中屏蔽系统键盘并弹出自定义的Dialog,无法获取用户选中的字符即Selection,造成的问题现象是用户不能选中中间某个位置修改输入值. 为了解决这一问题,需要在EditText的点击事件中获取Selection. 解决方法是:1.在EditText点击事件中获取用户点击位置,主要取其中的touchX值(这里主要讨

javascript 中英文字符长度和截断处理

因:javascript中的String类自带的length属性 和 substr()方法虽然能判断和截取出字符个数,但是对字节个数却没有处理的方法(众所周知,中文字符占两个字节,英文只占一个) 果:中英文字符在页面上的占位空间存在差异.中文的web开发人员不能一刀切,对中英文都截取同样长度的字符数. 网上大致有两种方法处理中英文字符.一种是把中文字符escape了,然后计算字节长度.一种是用正则表达式把中文字符转换2个字节的符号,然后计算长度 推荐第二种方法,简单明了,速度快,效率高. 按照第

字节大小转换为文本值描述, 仅显示用...

看代码... uses System.Math; // *************************************************************************** // 字节大小转换为文本值描述 // 刘志林 // 2017-11-06 // http://www.cnblogs.com/lzl_17948876/ // [email protected] // ---------------------------------------------

Excel—分离中英文字符

1.如下图: 2.提取中文字符为: 3.提取应为字符为: 4.说明: 该方法的原理利用了LENB和LEN计算方法的不同,LEN计算字符数,中英文都算作一个字符:LENB计算字节数,中文算两个字节,英文算一个字节.

处理字符串的一些js/jq方法(去除HTML,去除空格,计算真实长度,截取中英文字符)

stringObject.replace(regexp,replacement) regexp 必需.规定了要替换的模式的 RegExp 对象.请注意,如果该值是一个字符串,则将它作为要检索的直接量文本模式,而不是首先被转换为 RegExp 对象.replacement 必需.一个字符串值.规定了替换文本或生成替换文本的函数. 去除html标签:function del_html_tags(str){    var words = '';    words = str.replace(/<[^>

一个textview显示不同zize大小的文本

需求 类似于上图   首先思路一 : 采用两个textview 左右分开写  这样也可以实现  ,本人一直比较懒惰,喜欢思考一些特别的做法. 思路二: 采用html标签,使用类似下图 tv.setText(Html.fromHtml("<font>我的账户<span>(现金/红包/积分)</span></font>", imgGetter, null)); 但是这个改变不了文本大小,所以这个想法失败了,哪位要是这个思路有新办法告诉我啊:

Python统计字符串中的中英文字符、数字空格,特殊字符

# -*- coding:utf8 -*- import string from collections import namedtuple def str_count(s): '''找出字符串中的中英文.空格.数字.标点符号个数''' count_en = count_dg = count_sp = count_zh = count_pu = 0 s_len = len(s) for c in s: # 英文 if c in string.ascii_letters: count_en +=