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

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

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

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

          容健行@20077

          轉載請注明出處

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

          概述:

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

          當然字符串拆分還包括通過正則表達式來拆分,為了簡化問題,我們以單個字符做分隔的拆分,因為這種拆分用得最多。代碼使用C++來講解。

          問題:

          問題來源于實際,是之前我們組和其他組都有遇上的。先看一個例子,使用"|"拆分以下字符串,看起來怎么數都是分為48列,但我看到好幾個版本的字符串拆分函數卻報有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進制的編輯模式,看看它的編碼。

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

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

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

          解決方案:

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

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

          #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)

              {

                 // 如果前一個字符的第一位為1,且當前字符是在東亞文字的第二個字符,

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

                 std::string::size_type i = 1;

                 for (; i < pos; ++i)

                 {

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

                        break;

                 }

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

                 {

                     ++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版本測試

          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版本測試

          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" ";

              // 控制臺輸出不了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《談談Unicode編碼,簡要解釋UCS、UTF、BMP、BOM等名詞》



          主站蜘蛛池模板: 乡宁县| 泗阳县| 丹巴县| 武川县| 沿河| 庆元县| 楚雄市| 宣化县| 嘉鱼县| 永新县| 江陵县| 烟台市| 防城港市| 琼结县| 吉木乃县| 迭部县| 右玉县| 阿拉善盟| 昌平区| 华宁县| 祁连县| 马公市| 昭觉县| 南丰县| 敦煌市| 巩留县| 河间市| 平阴县| 建昌县| 玉屏| 曲水县| 深圳市| 黔东| 长寿区| 绍兴县| 温泉县| 盘山县| 旬阳县| 义乌市| 荆门市| 清丰县|