posts - 195, comments - 34, trackbacks - 0, articles - 1

          字符串拆分的中文處理問題zz

          Posted on 2010-04-19 01:47 小強(qiáng)摩羯座 閱讀(765) 評(píng)論(0)  編輯  收藏 所屬分類: C++ &VC

          字符串拆分的中文處理問題

          容健行@20077

          轉(zhuǎn)載請(qǐng)注明出處

          原文出處:http://www.devdiv.net/home/space.php?uid=125&do=blog&id=365

          概述:

          拆分一個(gè)字符串在程序中使用非常廣泛,特別是我們經(jīng)常跟表格打交道的程序員們。所謂拆分字符串,就是將一個(gè)字符串中間以某個(gè)(或某些)字符為分隔,拆分成多個(gè)字符串。如 std::string s = "abc | ddd | 中國(guó)";    如果以豎線“|”拆分,可以將這個(gè)字符串拆分成三個(gè)字符串。

          當(dāng)然字符串拆分還包括通過正則表達(dá)式來拆分,為了簡(jiǎn)化問題,我們以單個(gè)字符做分隔的拆分,因?yàn)檫@種拆分用得最多。代碼使用C++來講解。

          問題:

          問題來源于實(shí)際,是之前我們組和其他組都有遇上的。先看一個(gè)例子,使用"|"拆分以下字符串,看起來怎么數(shù)都是分為48列,但我看到好幾個(gè)版本的字符串拆分函數(shù)卻報(bào)有49列:

          "AGZGY1000004|200|劉瓅||20100101||OPRT10|1|0||AAGZ0Y100|0|0|24|0|0|0|0||-1|20030101|0|20991231||AGZGK6172888|200|曾曉翔||20100101||OPRT10|1|0||AAGZ0K617|0|0|24|0|0|0|0||-1|20061215|1|20061215||"

          原因分析:

          讓我們先把以上字符串放到UltraEdit中,并切換到16進(jìn)制的編輯模式,看看它的編碼。

          原因是原來的字符串拆分函數(shù)只是簡(jiǎn)單的查找“|”(編碼為0x7c,而沒有考慮到中文的處理(源代碼太多,且有好幾個(gè)版本,這里略去)。

          boss中,c++程序使用的編碼方式幾乎全為ansi,而在ansi中,表示中文是用兩個(gè)字符,且第一個(gè)字符是一個(gè)大于0x80的字符(字符的第一位為1),第二個(gè)字符為任意字符。這里引起一個(gè)問題:

          當(dāng)我們要分割字符串時(shí),假如用"|"(0x7c)作為分割符,當(dāng)分析上面這個(gè)字符遇到""(編碼為0xad,0x7c)這個(gè)字符時(shí),會(huì)把它第二個(gè)字符作為了分割符,結(jié)果就多出了一列。

          解決方案:

          問題原因找到了,重新寫了一下字符串拆分函數(shù)-Split,這里使用的方法是:找到分隔符后,再向前查找字符看一下它前一個(gè)字符是否為東亞文字的第一個(gè)字符編碼(編碼大于0x80)。

          考慮到以后支持unicode,這里使用了模板。以下可能不是最高效簡(jiǎn)單的實(shí)現(xiàn),但如果以后遇上這種問題,可以參考一下。

          #include "stdafx.h"

          #include <stdio.h>

          #include <tchar.h>

          #include <iostream>

          #include <string>

          #include <vector>

          #include <algorithm>

          #include <fstream>

          // unicode 分割策略

          inline

              bool __SplitPolicy(

              const std::wstring& s,

              const std::wstring& splitchar,

              std::wstring::size_type& pos)

          {

              pos = s.find_first_of(splitchar, pos);

              return pos != std::string::npos;

          }

          // ansi 分割策略

          inline

              bool __SplitPolicy(

              const std::string& s,

              const std::string& splitchar,

              std::string::size_type& pos)

          {

              pos = s.find_first_of(splitchar, pos);

              if (pos != std::string::npos)

              {

                 // 如果前一個(gè)字符的第一位為1,且當(dāng)前字符是在東亞文字的第二個(gè)字符,

                 // 則認(rèn)為該字符是東亞字的其中一個(gè)字符,要跳過,不作為分割符。

                 std::string::size_type i = 1;

                 for (; i < pos; ++i)

                 {

                     if (!((char)(s[pos - i]) & 0x80)) // 判斷第一位是否為1。(0x80的二進(jìn)制為 10000000)

                        break;

                 }

                 if (!(i % 2)) // 看一下當(dāng)前字符是否為東亞文字的第二個(gè)字符

                 {

                     ++pos;

                     __SplitPolicy(s, splitchar, pos);

                 }

              }

              return pos != std::string::npos;

          }

          template<typename char_type> inline

              int Split(

              const std::basic_string<char_type>& s,

              const std::basic_string<char_type>& splitchar,

              std::vector<std::basic_string<char_type> >& vec)

          {

              typedef std::basic_string<char_type>   string_t;

              typedef typename string_t::size_type   size_t;

              string_t tmpstr;

              size_t pos = 0, prev_pos = 0;

              vec.clear();

              while (__SplitPolicy(s, splitchar, pos))

              {

                 tmpstr = s.substr(prev_pos, pos - prev_pos);

                 vec.push_back(tmpstr);

                 prev_pos = ++pos;

              }

              size_t len = s.length() - prev_pos;

              if (len > 0)

                 vec.push_back(s.substr(prev_pos, len));

              return static_cast<int>(vec.size());

          }

          // ansi版本測(cè)試

          void testSplit()

          {

              std::vector<std::string> vec;

              const std::string str = "AGZGY1000004|200|劉瓅瓅||20100101||OPRT10|1|0||AAGZ0Y100|0|0|24|0|0|0|0||-1|20030101|0|20991231||AGZGK6172888|200|曾曉翔||20100101||OPRT10|1|0||AAGZ0K617|0|0|24|0|0|0|0||-1|20061215|1|20061215||a";

              const std::string sp = "|";

              int count = Split(str, sp, vec);

              for (std::vector<std::string>::const_iterator it = vec.begin(); it != vec.end(); ++it)

                 std::cout << *it << " ";

          }

          // unicode版本測(cè)試

          void testSplitW()

          {

              std::vector<std::wstring> vec;

              const std::wstring str = L"AGZGY1000004|200|劉瓅||20100101||OPRT10|1|0||AAGZ0Y100|0|0|24|0|0|0|0||-1|20030101|0|20991231||AGZGK6172888|200|曾曉翔||20100101||OPRT10|1|0||AAGZ0K617|0|0|24|0|0|0|0||-1|20061215|1|20061215||";

              const std::wstring sp = L"|";

              Split(str, sp, vec);

              const char head[3] = {0xff, 0xfe, 0};

              const wchar_t line[3] = L" ";

              // 控制臺(tái)輸出不了unicode字符,使用輸出到文件的方式

              std::ofstream fileOut("C:/out.txt");

              fileOut.write(head, 2);

              for (std::vector<std::wstring>::iterator it = vec.begin(); it != vec.end(); ++it)

              {

                 fileOut.write((const char*)it->c_str(), it->length() * 2);

                 fileOut.write((const char*)line, 2);

              }

          }

          int main()

          {

              testSplit();

              testSplitW();

          }

          參考:

          1http://unicode.org/

          2《談?wù)刄nicode編碼,簡(jiǎn)要解釋UCS、UTF、BMP、BOM等名詞》



          主站蜘蛛池模板: 拉萨市| 台北市| 兴业县| 柘荣县| 搜索| 广平县| 湛江市| 子洲县| 库车县| 巩留县| 读书| 大邑县| 牡丹江市| 区。| 罗田县| 承德市| 德庆县| 天气| 南郑县| 桦川县| 噶尔县| 弋阳县| 麦盖提县| 南通市| 偃师市| 广水市| 娱乐| 巴彦县| 广州市| 香格里拉县| 饶阳县| 徐汇区| 洮南市| 崇阳县| 济阳县| 姜堰市| 竹北市| 巍山| 惠东县| 陈巴尔虎旗| 土默特左旗|