2010-07-01 5 views
13

Извините, мой C/C++ не так уж хорош, но следующий существующий код выглядит как мусор даже для меня. У него также есть ошибка - не удается, когда str = "07/02/2010" завершено '\ 0' -. Я думаю, что вместо исправления ошибки он также может быть переписан. В Python это всего лишь 'kas\nhjkfh kjsdjkasf'.split(). Я знаю, что это код C-ish, но не может быть так сложно разбить строку! Придерживаясь одной и той же подписи, и без использования дополнительных библиотек, как я могу ее улучшить - сделать ее коротким и сладким? Я могу сказать, что этот код пахнет, например, из-за предложения else в конце.Лучший способ разделить строку на массив строк в C/C++ с использованием пробелов в качестве разделителя

ЛИНИЯ, ЧТО НЕ:

_tcsncpy_s(
    s.GetBuffer((int) (nIndex-nLast)), 
    nIndex-nLast, 
    psz+nLast, 
    (size_t) (nIndex-nLast) 
); 

строкой «07/02/2010» заканчивается «\ 0», он будет пытаться записать 11 символов в буфер, длина которого составляет всего 10 символов.

ПОЛНАЯ ФУНКЦИЯ:

#define 

// This will return the text string as a string array 
// This function is called from SetControlText to parse the 
// text string into an array of CStrings that the control 
// Gadgets will attempt to interpret 

BOOL CLVGridDateTimeCtrl::ParseTextWithCurrentFormat(const CString& str, const CGXStyle* pOldStyle, CStringArray& strArray) 
{ 
    // Unused: 
    pOldStyle; 

    // we assume that the significant segments are seperated by space 

    // Please change m_strDelim to add other delimiters 

    CString s; 

    LPCTSTR psz = (LPCTSTR) str; 

    BOOL bLastCharSpace = FALSE; 
    DWORD size = str.GetLength()+1; 

    // (newline will start a new row, tab delimiter will 
    // move to the next column). 
    // parse buffer (DBCS aware) 
    for (DWORD nIndex = 0, nLast = 0; nIndex < size; nIndex += _tclen(psz+nIndex)) 
    { 
     // check for a delimiter 
     if (psz[nIndex] == _T('\0') || _tcschr(_T("\r\n"), psz[nIndex]) || _tcschr(_T(" "), psz[nIndex]) 
      ||!_tcscspn(&psz[nIndex], (LPCTSTR)m_strDelim)) 
     { 
      s.ReleaseBuffer(); 
      s.Empty(); 
      // abort parsing the string if next char 
      // is an end-of-string 
      if (psz[nIndex] == _T('\0')) 
      { 
       if (psz[nIndex] == _T('\r') && psz[nIndex+1] == _T('\n')) 
        nIndex++; 

       _tcsncpy_s(s.GetBuffer((int) (nIndex-nLast)), 
        nIndex-nLast, 
          psz+nLast, 
          (size_t) (nIndex-nLast)); 
       CString temStr = s; 
       strArray.Add(temStr); 
       temStr.Empty(); 
       break; 
      } 

      else if (_tcscspn(&psz[nIndex], (LPCTSTR)m_strDelim) == 0 && !bLastCharSpace) 
      { 
       if (psz[nIndex] == _T('\r') && psz[nIndex+1] == _T('\n')) 
        nIndex++; 

       _tcsncpy_s(s.GetBuffer((int) (nIndex-nLast)), 
        nIndex-nLast, 
          psz+nLast, 
          (size_t) (nIndex-nLast)); 
       CString temStr = s; 
       strArray.Add(temStr); 
       temStr.Empty(); 
       bLastCharSpace = TRUE; 
       // abort parsing the string if next char 
       // is an end-of-string 
       if (psz[nIndex+1] == _T('\0')) 
        break; 

      } 
      // Now, that the value has been copied to the cell, 
      // let's check if we should jump to a new row. 
      else if (_tcschr(_T(" "), psz[nIndex]) && !bLastCharSpace) 
      { 
       if (psz[nIndex] == _T('\r') && psz[nIndex+1] == _T('\n')) 
        nIndex++; 

       _tcsncpy_s(s.GetBuffer((int) (nIndex-nLast)), 
        nIndex-nLast, 
          psz+nLast, 
          (size_t) (nIndex-nLast)); 
       CString temStr = s; 
       strArray.Add(temStr); 
       temStr.Empty(); 
       bLastCharSpace = TRUE; 
       // abort parsing the string if next char 
       // is an end-of-string 
       if (psz[nIndex+1] == _T('\0')) 
        break; 
      } 

      nLast = nIndex + _tclen(psz+nIndex); 


     } 
     else 
     { 
      // nLast = nIndex + _tclen(psz+nIndex); 
      bLastCharSpace = FALSE; 
     } 
    } 
    if (strArray.GetSize()) 
     return TRUE; 
    else 
     return FALSE; 
} 

РЕДАКТИРОВАТЬ: m_strDelim = _T(","); и эта переменная элемент используются только в этой функции. Я предполагаю, что теперь вижу точку токенизации - она ​​пытается разобрать дату и время ... подождите, есть еще! Вот код, который вызывает эту функцию ниже. Пожалуйста, помогите мне улучшить это. Некоторые из моих сотрудников утверждают, что C# делает их не более производительными, чем C++. Раньше я чувствовал себя идиотом за то, что не мог сказать обо мне.

// SetControlText will attempt to convert the text to a valid date first with 
// the help of COleDateTime and then with the help of the Date control and the 
// current format 

BOOL CLVGridDateTimeCtrl::ConvertControlTextToValue(CString& str, ROWCOL nRow, ROWCOL nCol, const CGXStyle* pOldStyle) 
{ 
    CGXStyle* pStyle = NULL; 
    BOOL bSuccess = FALSE; 

    if (pOldStyle == NULL) 
    { 
     pStyle = Grid()->CreateStyle(); 
     Grid()->ComposeStyleRowCol(nRow, nCol, pStyle); 
     pOldStyle = pStyle; 
    } 

    // allow only valid input 
    { 
     // First do this 
     CLVDateTime dt; 

     if (str.IsEmpty()) 
     { 
      ; 
      // if (Grid()->IsCurrentCell(nRow, nCol)) 
      // Reset(); 
      bSuccess = TRUE; 
     } 
     else if (dt.ParseDateTime(str,CLVGlobals::IsUSDateFormat()) && (DATE) dt != 0) 
     { 
      SetDateTime(dt); 
      if (m_bDateValueAsNumber) 
       str.Format(_T("%g"), (DATE) dt); 
      else 
       str = dt.Format(); 
      bSuccess = TRUE; 
     } 
     else 
     { 
      // parse the string using the current format 
      CStringArray strArray; 
      if (!ParseTextWithCurrentFormat(str, pOldStyle, strArray)) 
       return FALSE; 

      UpdateNullStatus(m_TextCtrlWnd); 

      SetFormat(m_TextCtrlWnd, *pOldStyle); 

      int nArrIndex = 0; 
      for(int i=0; i<m_TextCtrlWnd.m_gadgets.GetSize(); i++) 
      { 
       int val = m_TextCtrlWnd.m_gadgets[i]->GetValue(); 
       // s.Empty(); 
       if(m_TextCtrlWnd.m_gadgets[i]->IsKindOf(RUNTIME_CLASS(SECDTNumericGadget))) 
       { 
        // TRACE(_T("The value %s\n"), strArray[nArrIndex]); 
        ((CLVDTNumericGadget*)m_TextCtrlWnd.m_gadgets[i])->m_nNewValue = _ttoi(strArray[nArrIndex]);  
        nArrIndex++; 
        if (nArrIndex>strArray.GetUpperBound()) 
          break; 
       } 
       else if(m_TextCtrlWnd.m_gadgets[i]->IsKindOf(RUNTIME_CLASS(SECDTListGadget)) && val!=-1) 
       { 
        int nIndex = ((CLVDTListGadget*)m_TextCtrlWnd.m_gadgets[i])->FindMatch(strArray[nArrIndex], ((CLVDTListGadget*)m_TextCtrlWnd.m_gadgets[i])->GetValue()+1); 
        if (nIndex!=-1) 
        { 
         // TRACE(_T("The value %s\n"), strArray[nArrIndex]); 
         ((CLVDTListGadget*)m_TextCtrlWnd.m_gadgets[i])->SetValue(nIndex); 
         nArrIndex++; 
         if (nArrIndex>strArray.GetUpperBound()) 
          break; 
        } 

       } 

       CLVDBValue dbDate = m_TextCtrlWnd.GetDateTime(); 
       if (dbDate.IsNull()) 
        str = _T(""); 
       else 
       { 
        CLVDateTime dt = (CLVDateTime)dbDate; 
        if (m_bDateValueAsNumber) 
         str.Format(_T("%g"), (DATE) dt); 
        else 
         str = dt.Format(); 
       } 
      } 
      bSuccess = TRUE; 
     } 
    } 

    if (pStyle) 
     Grid()->RecycleStyle(pStyle); 

    return bSuccess; 
} 

ответ

4

Лучший способ сделать это будет использовать strtok. Эта ссылка должна быть самоочевидной в отношении того, как ее использовать, и вы также можете использовать несколько разделителей. Очень удобная функция C.

+1

+1, но я уверен, что у кого-то будет сумасшедшее решение на C++, включая синтаксис bizarro, чтобы забрать ваши upvotes. –

+1

Это хорошее решение для C. Если вы хотите использовать C++, нет ничего «сумасшедшего» в отношении кодирования сильных сторон языка. – Cogwheel

+11

Если 'strtok' - правильный ответ, вы обычно задавали неправильный вопрос. –

6

В C++, это, вероятно, проще всего использовать stsringstream:

std::istringstream buffer("kas\nhjkfh kjsdjkasf"); 

std::vector<std::string> strings; 

std::copy(std::istream_iterator<std::string>(buffer), 
      std::istream_iterator<std::string>(), 
      std::back_inserter(strings)); 

Я не пытался придерживаться точно такой же подписью, в основном потому, что большинство из них не является стандартным, поэтому он не применимо к C++ вообще.

Другой возможностью было бы использовать Boost::tokenizer, хотя очевидно, что это связано с другой библиотекой, поэтому я не буду пытаться ее охватить более подробно.

Я не уверен, что это квалифицируется как «синтаксис bizarro» или нет. Я, возможно, придется немного поработать над этой частью ...

Edit: Я получил это - инициализировать вектор вместо:

«Bizarro» часть является то, что без дополнительных скобок вокруг первый аргумент, это вызовет «самый неприятный синтаксический разбор», поэтому он будет объявлять функцию вместо определения вектора. :-)

Редактировать 2: Насколько правдоподобно право на вопрос, кажется почти невозможным ответить напрямую - это зависит от слишком большого количества типов (например, CGXStyle, CLVDateTime), которые не являются ни стандартными, ни объясняемыми. Я, во-первых, не могу полностью его проследить. Оффлайн, это выглядит довольно плохой дизайн, позволяя пользователю вводить вещи, которые более или менее неоднозначны, а затем пытается разобраться в беспорядке. Лучше использовать элемент управления, который позволяет вводить однозначный ввод, и вы можете просто прочитать некоторые поля, которые содержат дату и время напрямую.

Edit3: код, чтобы сделать разделение, который также рассматривает запятые в качестве разделителей можно сделать так:

#include <iostream> 
#include <locale> 
#include <algorithm> 
#include <vector> 
#include <sstream> 

class my_ctype : public std::ctype<char> { 
public: 
    mask const *get_table() { 
     // this copies the "classic" table used by <ctype.h>: 
     static std::vector<std::ctype<char>::mask> 
      table(classic_table(), classic_table()+table_size); 

     // Anything we want to separate tokens, we mark its spot in the table as 'space'. 
     table[','] = (mask)space; 

     // and return a pointer to the table: 
     return &table[0]; 
    } 
    my_ctype(size_t refs=0) : std::ctype<char>(get_table(), false, refs) { } 
}; 

int main() { 
    // put our data in a strea: 
    std::istringstream buffer("first kas\nhjkfh kjsdjk,asf\tlast"); 

    // Create a ctype object and tell the stream to use it for parsing tokens: 
    my_ctype parser; 
    buffer.imbue(std::locale(std::locale(), &parser)); 

    // separate the stream into tokens: 
    std::vector<std::string> strings(
     (std::istream_iterator<std::string>(buffer)), 
     std::istream_iterator<std::string>()); 

    // copy the tokes to cout so we can see what we got: 
    std::copy(strings.begin(), strings.end(), 
     std::ostream_iterator<std::string>(std::cout, "\n")); 
    return 0; 
} 
+0

Прохладный, мы используем VS2010 для его компиляции, поэтому 'Boost' немного растягивается, но я уверен, что доступно множество библиотек. –

+8

Подождите, Джерри, где указать список символов для tokenize? См. Ответ, например, Beh Tou Cheh. он имеет: 'strtk :: parse (data,", \ r \ n ", str_list);'. –

+1

Они указаны в 'locale', используемом потоком. По умолчанию это будет только пробел, но вы можете создать языковой стандарт с «ctype facet», который использует все, что вы хотите. http://stackoverflow.com/questions/1894886/parsing-a-comma-delimited-stdstring/1895584#1895584 –

0

PARSING строки в C/C++ редко оказывается простым делом. Метод, который вы опубликовали, выглядит так, что в нем задействовано немало «истории». Например, вы заявляете, что хотите разбить строку на пробел. Но сам метод, по-видимому, использует переменную-член m_strDelim как часть решения о расщеплении. Просто замена метода может привести к другим неожиданным проблемам.

Использование существующего класса токенинга such as this Boost library может упростить некоторые вещи.

+3

«Разбор строк на C/C++ редко оказывается простым». Это простой вопрос, но решения, такие как синтаксический анализ, обычно включают в себя обработку строк по характеру и циклам. То есть Я не могу придумать что-то (стандартное), как функция split() python в C++ .... – SigTerm

1

Совершенно очевидно, что при сортировке этой проблемы необходимо использовать библиотеки Qt. Если вы используете KDE, они уже установлены. Класс QString имеет функцию разделения членов, которая работает как версия python. Например

QString("This is a string").split(" ", QString::SkipEmptyParts) 

возвращает QStringList из QString S:

["This", "is", "a", "string"] 

(в Pythonic синтаксиса). Обратите внимание, что требуется второй аргумент, иначе слова должны быть разделены несколькими пробелами, каждый из которых будет возвращен.

В целом я нахожу с помощью библиотек Qt большую часть простоты python, например. простой синтаксический анализ строк и итерация списка, можно обрабатывать с легкостью и с силе C++.

+4

Мы магазин MSFT, поэтому я могу использовать любые стандартные библиотеки с VS2010. Мне нравится Linux, open source и т. Д., Но я не могу просто установить произвольные библиотеки. Я мог бы украсть пару файлов .h и .cpp, если это разрешит лицензия. –

13

String Toolkit Library (Strtk) имеет следующее решение проблемы:

#include <string> 
#include <deque> 
#include "strtk.hpp" 
int main() 
{ 
    std::string data("kas\nhjkfh kjsdjkasf"); 
    std::deque<std::string> str_list; 
    strtk::parse(data, ", \r\n", str_list); 
    return 0; 
} 

Больше примеров можно найти Here

+0

Хм ... Интересно, могу ли я украсть только пару заголовков и файлов cpp без установки всего этого. –

+18

@Hamish: Не стесняйтесь брать то, что вам нравится, все под CPL. Если у вас нет Boost или просто используйте vanilla C++ с STL, вы можете просто прокомментировать #define ENABLE_LEXICAL_CAST #define ENABLE_RANDOM #define ENABLE_REGEX, и все должно работать, все это объяснено в файле readme.txt. – 2010-07-02 03:43:53

0

Вы можете использовать boost::algorithm::split. I.e .:

std::string myString; 
std::vector<std::string> splitStrings; 
boost::algorithm::split(splitStrings, myString, boost::is_any_of(" \r\n")); 
+0

+1 Библиотека алгоритмов Boost String Algorithms необходима для строковых операций! –

+0

Мне очень жаль, что я не знал, почему люди этим занимаются. –

+0

Я не спустил вниз. Полагаю, это из-за моего комментария (не оригинального сообщения), что я предпочитаю использовать только стандартные библиотеки ... –

0

Лучший способ, чем мой другой ответ: TR1's функция регулярного выражения. Here's небольшой учебник, чтобы вы начали. Этот ответ - C++, использует регулярные выражения (это, пожалуй, лучший/самый простой способ разделить строку), и я использовал его сам недавно, поэтому я знаю, что это хороший инструмент.

Смежные вопросы