【问题标题】:Split char * with strtok in C++在 C++ 中使用 strtok 拆分 char *
【发布时间】:2014-06-05 20:57:49
【问题描述】:

我必须为一门课程做这个练习。

class Cadena{
protected:
char * s;
bool I;
}

char * s 具有以下样式:“text1:text2::text3:” 我需要通过拆分 char * 来生成一个列表 (char *),当有一个 : 时,每次我得到一个 :: 我必须将“@EMPTY@” 添加到我正在生成的列表中,如果它以: 结尾,我需要在列表末尾添加“@EMPTY@”

现在我有这两种方法,dividirTupla 工作得很好,可以做我需要的一切,但你可以看到它不是“最好”的代码,而split 做了我需要的大部分,但我无法意识到我收到的 char * 何时有 ::,所以我可以在正确的位置将 “@EMPTY@” 添加到我的列表中。

有什么办法可以做到吗?所以第二种方法可以工作,而不是使用第一种混乱的方法。

    Lista<Cadena>* Cadena::dividirTupla(){
        if(s!=NULL){
            Lista<Cadena> *ret=new ListaImp<Cadena>();
            int ppio=0;
            int fin=0;
            //Go over the char * in order to find the :
            for(int i=0; i<strlen(s); i++){
                if(s[i]==':'){
                    //If there is a ::
                    if(ppio==fin){
                        ret->AgregarFin("@EMPTY@");
                    }else{
                        //If we find a : we add the substring to our list
                        string a=((string)s).substr(ppio,fin-ppio);
                        char * dato = new char[a.length() + 1];
                        strcpy(dato,a.c_str());
                      //It adds the char * to the list
                        ret->AgregarFin(dato);
                        delete dato;
                    }
                    fin++;
                    ppio=fin;
                }else{
                    fin++;
                }
            }
            //Add the last text
            if(ppio<strlen(s)){
                string a=((string)s).substr(ppio,strlen(s));
                char * dato = new char[a.length() + 1];
                strcpy(dato,a.c_str());
                ret->AgregarFin(dato);
                delete dato;
            }
            //If it ends in : add the @EMPTY@
            if(s[strlen(s)-1]==':'){
                ret->AgregarFin("@EMPTY@");
            }
            cout<<*ret;
            return ret;
        }else{
            return NULL;
        }
    }

    Lista<Cadena>* Cadena::split(){
        if(s!=NULL){
            char * aux=strtok(s,":");
            Lista<Cadena> *ret=new ListaImp<Cadena>();
            while(aux != NULL){
                ret->AgregarFin(aux);
                aux= strtok(NULL, ":");
            }
            return ret;
        }else{
            return NULL;
        }
    }

【问题讨论】:

  • 真是一场噩梦。您知道,如果您有相同的要求,但被告知使用 C++(使用标准库)执行此操作,这仍然是一个很好的练习。相反,您会被告知使用这种容易出错的低级逻辑来执行此操作。您不妨在“C”中执行此操作。真可惜。
  • 看起来他们是用 C 来做的......(注意:C++ 不是“C with lists”!)
  • 哦,好的,我明白了。我更改了 C 的标签。谢谢!
  • 不,代码是 C++,但它不使用 C++ 必须提供的任何功能。它基本上是带有一些 C++ 关键字的“C”(例如newdelete),而且它还有这个未知的Lista 类型。一件事,请在整个代码中使用std::string。不要为了它而开始使用char* new[]。您正在使用std::string,然后出于某种奇怪的原因,您立即切换回使用new char[]。为什么?
  • 好的。我读了你的要求。它只是一个以冒号分隔的字符串解析器。您需要做的就是对字符串进行标记,如果没有找到标记,则使用单词@EMPTY@ 重建一个新字符串。有很多更好的方法可以使用诚实的 C++,而不是 C 和类。如果您对面向 C++ 的答案感兴趣,请说出来。否则,我认为没有人会像现在这样尝试浏览您的代码。

标签: c++ split char strtok


【解决方案1】:

只是为了证明 Paul 错了;)你能用 strstr 代替吗?

如果没有,我就自己重写strtok,自己返回一个结构,这样在调用代码中可以区分NULL错误情况和0长度匹配。

使用 strstr,大致类似于(尽管会有诸如“:xx:xx”行之类的错误供您修复)..

扫描分隔符“:”,找到时重新扫描一个重复的“::” 如果我们有重复,请复制 EMPTY 标记,然后再次查找“::”

copyFromUntil,应该保存从第一个到第二个字符 * 指针的令牌。

strstr - 找到一个子字符串见http://linux.die.net/man/3/strstr

//
// Handle stuff like "", "xxx", "xx::xx:::xx:xx::::xxx" and "xx::xx:::xx:xx::::xxx:"
//
// first find a ":", then check if it's "::"
char *delim, *repeateddelim;
while ((delim  = strstr( s, ":") != NULL) {

    // Process from s..delim
    copyFromUntil( s, delim);

    // Scan for repeated delimiter's eg) : in "xx::xx:::xx:xx::::xxx"
    while (delim == (repeateddelim = strstr( delim, "::")) {

        // Insert the empty delimitter marker
        copyFromUntil( EMPTY_STR, EMPTY_END);

        /* Resume scan from 2nd ':'
        s = delim = repeateddelim;
    }
    // At a single ":" now, move past, saving delim pos
    lastdelim = delim;
    s= delim+1;
}
// From start of line or last ":" to EOL
if (s) {
    if (strlen(s) == 0 && (s == lastdelim+1) {    //  one past a delimiter?
        copyFromUntil( EMPTY_STR, EMPTY_END);
    } else {
        copyFromUntil( s, s + strlen(s));
    }
}

【讨论】:

  • 谢谢 Rob,我会试试这个或类似的东西,看看它是否有效
  • 如果允许 2 次通过,那么您可以在 :: 上使用 strstr,将字符串从 xxx:xx::xx 重建为 xxx:xx:@EMPTY@:xx,然后使用 strtok 方法你已经写过了。仍然需要处理 :xx: 案件。
猜你喜欢
  • 2021-09-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-27
  • 1970-01-01
  • 2015-12-26
  • 1970-01-01
相关资源
最近更新 更多