トップページ⇒tech
37コメント21KB

Cの文字列切関数

■ このスレッドは過去ログ倉庫に格納されています
0001名無しサンプリング@48kHzNGNG
cの関数で
ab=bc=def
のような文字列を
*char[0] = "ab";
*char[1] = "bc";
*char[2] = "def";

のような配列の変数に入れたいのですが
=の数を数えた上で(この場合は3)
char ** split_num(char * str@`int n)
のようにして呼び出してみているのですが
うまくいきません。わかる方どうぞお教えください。
0002名無しさん@C厨房NGNG
よくわからんけどstrtokじゃ駄目なのかなぁ?
00032NGNG
ハズしてますか?
#include <stdio.h>
int main()
{
char str[10]="ab=bc=def";
char *p[3];
p[0]=strtok(str@`"=");
p[1]=strtok(NULL@`"=");
p[2]=strtok(NULL@`"=");
printf("%s"@`p[2]);//defと表示される
return 0;
}
0004デフォルトの名無しさんNGNG
>>1
Cでやるのは面倒だぞ!
Perlとかなら簡単だが。

char** split_num(char* str @` int n)
{
char** ch;

ch = (char**)malloc(3*sizeof(char*));
ch[0] = (char*)malloc(3);
ch[1] = (char*)malloc(3);
ch[2] = (char*)malloc(4);
strcpy(ch[0] @` "ab");
strcpy(ch[1] @` "bc");
strcpy(ch[2] @` "def");
return ch;
}

これで多分OK(テストしてないんで分からん。寝起きだし)
あと split_num の返値の char** が要らなくなったらメモリを破棄する。

void split_num(char** ch)
{
free(ch[0]);
free(ch[1]);
free(ch[2]);
free(ch);
}

----------------------------

悪い例

char** split_num(char* str @` int n)
{
char* ch[3];

ch[0] = (char*)malloc(3);
ch[1] = (char*)malloc(3);
ch[2] = (char*)malloc(4);
strcpy(ch[0] @` "ab");
strcpy(ch[1] @` "bc");
strcpy(ch[2] @` "def");
return ch;
}

これだと一見うまくいくように見えるが。
ch はスタック変数なので関数を抜けた時点で無効になるのでうまくいかない。
00051NGNG
ありがとうございます。
とりあえず
strtokを使ってやってみました。
char value[256][256];
char * tok;
char str[] = "ab=cd=ef";
int value_num = 0;
tok = strtok(str@`delimita);
while(tok != NULL){
// printf("%s\n"@`tok);
strcpy(value[value_num]@`tok);
tok = strtok(NULL@`delimita);
value_num++;
}
このようなかたちでできました。
が、これを関数で受け渡しができないのです。
デリミタと最初の文字列(str)を渡して
配列の配列を返すようにしたいのですが、
その場合はchar **を返すようにするのですよね。

>3さん
そうですよね。僕はperlからCへのクチなので、
文字列に関するperlの便利さを実感しています。
0006デフォルトの名無しさんNGNG
X delimita
O delimiter

あとは分割された文字列をコピーして返せばいいんじゃないの?
0007デフォルトの名無しさんNGNG
strtokの話題って2ch内でも時々あったよ。
0008デフォルトの名無しさんNGNG
>>1
=が連続している場合や端に=がある場合にどうしたい?
perlのsplitのような仕様を望むのなら、strtokでは駄目。
strtokは連続するデリミタを単一のものと扱う。
まあ、それ以外にも、単純な実装だとリエントラントにならないとか、
strtokはANSI/ISO標準とは言え、とかく問題の多いライブラリ関数ではある。
後は、マルチバイトキャラに対応する必要があるかどうかだな。

strtokでいいのなら、1が求めているインタフェイスとはやや
違うが、例えば以下の方法で行える。これの利点は、mallocやコピーが
不要なことだ(つまり効率がいい)。

int nsplit(char **dst@` char *src@` const char *delim@` int max)
{
  int i;
  char *tok;

  for (i = 0@` tok = strtok(src@` delim);
    (i < max) && tok;
    tok = strtok(NULL@` delim))
  {
    dst[i++] = tok;
  }
  return i;
}

次のようにして使う。

int main()
{
  char buff[1024];
  char *field[3];
  char *p;
  int i@` num;

  while (fgets(buff@` sizeof buff@` stdin)) {
    if (p = strchr(buff@` '\n'))
      *p = '\0';
    num = nsplit(field@` buff@` "="@` 3);
    for (i = 0; i < num; ++i)
      puts(field[i]);
  }
return 0;
}

0009忘却の名無しさんNGNG
C++のstringクラスについてなかったですか?
記憶あやふや。
VBにはあったんだよね。
0010デフォルトの名無しさんNGNG
>C++のstringクラスについてなかったですか?

なかったはず
0011名無しさん@お腹いっぱい。NGNG
で、>>5に対する答えはどうなってるんですか?
一応、こうすれば上手く動いてるように見えますが
代入をstrcpyにするとエラーになります・・。
一つ正しい方法を教えてください。
#include <stdio.h>
#include <string.h>
char ** split_num(char * str@`int n);
char ** split_num(char * str@`int n)
{

char *value[256];
char delimita[2]="=";
char * tok;
int value_num = 0;
tok = strtok(str@`delimita);
while(tok != NULL){
value[value_num]=tok;
tok = strtok(NULL@`delimita);
value_num++;

}
return value;
}
int main()
{
char temp[256]="ab=cd=def";
char **p;
p=split_num(temp@`3);
printf("%s"@`p[1]);
return 0;
}
0012名無しさん@お腹いっぱい。NGNG
>代入をstrcpyにするとエラーになります・・。
メモリを確保してないポインターにコピーすりゃ普通落ちるでしょ。
00138NGNG
std::stringには無い。
VBも6からとかじゃなかったかな。
まあ何にせよ、見ての通り実際には別に関数化する程でも無いから、
直接strtokを使用するCプログラマがほとんどだと思う。
なおFreeBSDには、少なくともstrtokよりはマシなstrsepという関数が
あって、これは連続するデリミタを別々のものと扱う(間に空文字列が
入っているものとする)。ただし勿論マルチバイト文字には対応していない。
俺はこの手の仕事が必要な場合は、strsepを下敷きにした関数を使うことが
多い。
001411NGNG
>12
いえ、それはわかってるんですが、やりかたがわからないんです。
具体的なコードでご教授いただければ幸いです。
というか11のコードは正しいんですか?(とてもそうは思えないですが。)
00158NGNG
>>14
勿論autoの配列であるvalueへのポインタを返している以上
正しくは無い。valueに対してポインタを代入するだけで済ませるのは、
間違いではない。いずれにせよ、元のstrは壊される。これを良しと
するかどうかは単にポリシー/仕様の問題だ。
どうしてもコピーを生成することに固執するのなら、次のように
するのだね。ただしこれは8のものに比べて非常に非効率で、
しかも後でメモリを解放しなければならない。
strdupは標準の関数では無いが、たぶんついているだろう。

char **split_num(const char *s@` int n)
{
  int i = 0;
  char *tok;
  char *w = strdup(s);
  char **field = malloc((n+1) * sizeof(char*));
  for (tok = strtok(w@` "="); (i < n) && tok; tok = strrok(NULL@` "="))
    field[i++] = strdup(tok);
  field[i] = NULL;
  free(w);
  return field;
}
001611NGNG
8さん、ありがとうございます。
>>15
なるほど、勉強になります。
strdupはbcc5.5にもついてました。
8のコードも検討してみます。
ちなみに私は1さんではありません。
0017名無しさん@お腹いっぱい。NGNG

これで完璧!


#include <stdio.h>
#include <string.h>
#include <malloc.h>


#define LIST_INC 20


typedef struct
{
 char** tok;
 int   num;
 int   list_max;
} TOK_STRUCT;


void split_num_add(TOK_STRUCT* tok_struct @` char* tok) // 追加
{
 int  length;
 char** new_tok;

 length = strlen(tok);
 if(length > 0)
 {
  if(tok_struct->num >= tok_struct->list_max)
  {
   new_tok = (char**)malloc((tok_struct->list_max+LIST_INC)*sizeof(char*));
   if(tok_struct->tok != NULL)
   {
    memcpy(new_tok @` tok_struct->tok @` tok_struct->list_max*sizeof(char*));
    free(tok_struct->tok);
   }
   tok_struct->tok = new_tok;
   tok_struct->list_max += LIST_INC;
  }
  tok_struct->tok[tok_struct->num] = (char*)malloc(length+1);
  strcpy(tok_struct->tok[tok_struct->num] @` tok);
  tok_struct->num++;
 }
}

void split_num_cleanUp(TOK_STRUCT* tok_struct)    // メモリ破棄
{
 int  i;

 if(tok_struct->tok != NULL)
 {
  for(i=0 ; i<tok_struct->num ; i++) free(tok_struct->tok[i]);
  free(tok_struct->tok);
 }
 memset(tok_struct @` 0 @` sizeof(TOK_STRUCT));
}

void split_num(TOK_STRUCT* tok_struct @` char *str @` char* delimita)   // 分割
{
 char*  tok;

 tok = strtok(str@`delimita);
 while(tok != NULL)
 {
  split_num_add(tok_struct @` tok);
  tok = strtok(NULL @` delimita);
 }
}

int main()
{
 char     temp[256]="ab=cd=def";
 TOK_STRUCT  tok_struct;
 int     i;

 memset(&tok_struct @` 0 @` sizeof(TOK_STRUCT));
 split_num(&tok_struct @` temp @` "=");
 for(i=0 ; i<tok_struct.num ; i++) printf("%s\n" @` tok_struct.tok[i]);
 split_num_cleanUp(&tok_struct);
 return 0;
}

001811NGNG
>>17
全然わからないけど、とりあえずスゴイ・・。
トークンの処理にこれだけやらないと完璧とは言えないのですか・・。
なんか頭がクラクラしてきました。
ありがたく保存させてもらって寝ます。おやすみなさい・・。
(ゴミレスなのでsageさせてもらいます。)
0019デフォルトの名無しさんNGNG
>>17
あんだけの行数割いて、結局strtok使うのか。(藁
それと空白や引用符とかどうすんの?

スタック領域のみでmainのargc@`argv互換のでも作った方が後々良いよ。
インターフェースはこれだけ。
int /* <- argc */
getarg(char **argv@` int argvn@` char *argbuf@` const char *str@` const char *dlm);
0020デフォルトの名無しさんNGNG
>>17
STL使え。
STLの凄さを理解してない奴って多いよな・・・

#include <stdio.h>
#include <string.h>
#include <vector>

typedef std::vector<char*> TokType;

void split_num(TokType* toks@` char *str @` char* delimiter)   // 分割
{
 char*  tok;

 tok = strtok(str@`delimiter);
 while(tok != NULL)
 {
  toks->push_back(tok);
  tok = strtok(NULL @` delimiter);
 }
}

int main()
{
 char temp[256]="ab=cd=def";
 TokType toks;
 int i;

 split_num(&toks @` temp @` "=");
 for(i=0 ; i<toks.size() ; i++) printf("%s\n" @` toks[i]);
 return 0;
}
002119NGNG
んじゃ、わてはさっき言ったやつね。
int /* <-argc */
split_num(char **argv@` int argvn@` char *str@` char* delimiter) {
 char* tok = strtok(str@` delimiter);
 int ac = 0;
 av[ac] = 0;
 while (tok && ac < argvn - 1) {
  av[ac++] = tok;
  av[ac] = 0;
  tok = strtok(NULL @` delimiter);
 }
}
int main() {
 char temp[256]="ab=cd=def";
 {
  char *av[128];
  int i@` ac = split_num(av@` sizeof(av)/sizeof(av[0])@` "=");
  for(i=0 ; i<ac ; i++) printf("%s\n" @` toks[i]);
  return 0;
}
002219NGNG
s/toks/av/
0023デフォルトの名無しさんNGNG
>>19
スタック領域にこだわる理由は?
002419NGNG
>>23
21は20真似してstrtok使ってるから、もはやどうでも良いけど、スレッドセーフと効率優先。
それから、split_numの最後に書き忘れたけど、 return ac; 付けといてね。
002517NGNG
>>19
>それと空白や引用符とかどうすんの?
意味分からんぞ!

>>20
STLってなんだ?
見た所JavaのVectorクラスと同じようだが確保したメモリは破棄しなくていいのか?
Vector内部でリスト用のメモリ使ってないのか?
それとも不用になったら自動的に破棄されるとか?
0026デフォルトの名無しさんNGNG
>>25
自動的に破棄されるし、メモリの確保なんかも自動でやるぞ。
002719NGNG
>>25
char temp[256]=" \"ab\" = 'cd' = def";
こういう場合どうすんだ?って事。
それから、20のは本質的に21としてる事は変わらんのであれで良い。
わかりやすく言うと、TokTypeは(char *)を保持してるの。
002817NGNG
>>26
そうなんだ。
STLって凄いな。
てっきりバグだと思ってツッコミいれたのに・・・
002917NGNG
>>27
>char temp[256]=" \"ab\" = 'cd' = def";
全然意味分からん。
今コンパイルしてみたが、

"ab"
'cd'
def

って表示されたが何か問題でも?
003019NGNG
>>29
結果見て問題が理解できなかったって事は、17にとってそれは問題では無かったって事。
0031デフォルトの名無しさんNGNG
ところで、STLはC++だが、使っていいのか?
1の質問が学校の宿題だったら多分まずいだろ。
0032忘却の名無しさんNGNG
ホントだCってなってる・・・。
0033デフォルトの名無しさんNGNG
>>21
は8と何が違うのかわからんな。
003419NGNG
ほんとだ。>33
全然見てなかったよ、すまそ。
0035デフォルトの名無しさんNGNG
>>20 に激しく同意。
標準関数や標準クラスを知らないで仕事をしているなんて信じられませんよ。

しかしサンプルはこうでしょう。

class split
{
private:
vector<string> m_token;
public:
split(const string &s@` const string &d);
split(const split &); //省略
split &operator=(const split &); //省略
string operator[](int i) const { return m_token[i];}
int size() const { return m_token.size(); }
};

split::split(const string &s@` const string &d)
{
auto_ptr<char> w(new char[s.size()+1]);
strcpy(w.get()@` s.c_str());
for (char *token = strtok(w.get()@` d.c_str()); token; token = strtok(NULL@` d.c_str()))
m_token.push_back(string(token));
}

int main(int argc@` char* argv[])
{
split temp("ab=cd=def"@` "=");
for (int i = 0; i < temp.size(); i++)
cout << temp[i];
return 0;
}

なに? C言語? 聞こえんなぁ。
0036デフォルトの名無しさんNGNG
>>35
>なに? C言語? 聞こえんなぁ。

残念ながらstrtokは「C言語」の関数ですが。
しかも使用をあまり推薦されてない中での筆頭(笑
それにしてもなんでstrtokのサンプルばっかり?
0037!35NGNG
>>36
じゃあ、こんなのはどうだ?
std::stringに対して使えるstrtok関数もどきだ。

string GetToken(string& line@` const char* delims)
{
&nbsp;&nbsp;&nbsp;string ret;

&nbsp;&nbsp;&nbsp;string::size_type index = line.find_first_of(delims);
&nbsp;&nbsp;&nbsp;if(index == string::npos){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret = line;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line = "";
&nbsp;&nbsp;&nbsp;}else{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret = line.substr(0@` index);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line = line.substr(index + 1);
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;return ret;
}

string GetToken(string& line@` char delim)
{
&nbsp;&nbsp;&nbsp;string ret;

&nbsp;&nbsp;&nbsp;string::size_type index = line.find(delim);
&nbsp;&nbsp;&nbsp;if(index == string::npos){
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret = line;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line = "";
&nbsp;&nbsp;&nbsp;}else{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ret = line.substr(0@` index);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;line = line.substr(index + 1);
&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;&nbsp;return ret;
}
■ このスレッドは過去ログ倉庫に格納されています