家・車以外で、人生で一番奮発した買い物

読み込んだ文字列 s[40] から実数値の文字列を切り出し,個別の文字配列に保存する操作がしたいです.
ファイルから1行分の文字列を読み込み,3つの実数値(文字列)を切り出し,文字配列にコピーする,という操作なのですが,例えば,

0.2¥t0.0¥t1.0¥0

という文字配列があったとして
最初の文字を切り出すのに

char s[40], ts[15]; /*file1から一行読み込んだ文字列 s[40],これからコピーする文字配列ts[15]*/
int i, j;
...
fgets(s, 39, file1);
i = 0;
for(j = 0; s[i] != '\t'; j++, i++){
ts[j] = s[i];
}/*1つ目のタブ文字に出会うまで s[] の文字を ts[]にコピーする*/
ts[j] = /*切り出した文字列の最後にヌル文字を代入*/

printf("%s\n", ts); /*0.2が画面表示される*/

というようなプログラムがかけるかと思います.
しかし,2つ目と3つ目の実数値を取り出す際,どのように書けるかがわかりません.
(初期値 i の値を変えるのかな,という想像はしているのですが...)

どのように表せばいいか教えていただけたら幸いです.
わかりづらい箇所があったら申し訳ないです.
よろしくお願いします.

質問者からの補足コメント

  • こういうことでしょうか?
    見づらかったら申し訳ないです.

    「プログラミング C言語 意味がわからない」の補足画像1
      補足日時:2020/07/31 21:53

A 回答 (6件)

No.1です。



えっと。。。
file1をfgetしたらforループに入り、その中ですぐfile1をfgetする意味は何でしょう???
そして更にforループが3つも作らねばならない理由は何でしょう?

まずはいきなりソースコードを書くのではなく、日本語の箇条書きやチャート図などを使って処理手順を日本語で書き表してみましょう。
そしてそれをじっくり読み直し、「この通り処理すれば目的のことが出来る」と確認出来たらその通りにコーディングしてみましょう。
なお、その箇条書きやチャート図にはソースコードレベルの詳細さは求めません。例えば、「変数〇〇を定義する」とか「〇〇用のカウンター変数××をインクリメントする」とかいった詳細なことは不要です。
「〇〇ファイルから1レコード読み込む」とか「読み込んだレコードの先頭から最後まで1もじずつ以下の処理を行う」といった感じで、どういう順番で、何を、そう処理するか、がハッキリわかるようにまとめます。

参考まで。
    • good
    • 0
この回答へのお礼

なるほど...今まで直接コードを書き始めていました.
このコードに関しても一度紙に書いてからやり直そうと思います.
ありがとうございます!

お礼日時:2020/08/01 16:07

No.5です。



> 今までわからないものはブログなどを見て書いていたので,もう一度やり直してみようと思います.

何を目的にどういった方法でプログラミング言語を学ばれておられるのかわかりませんが、「どういう入力を受けてどういう出力をおこなうのか」をしっかり定め、それを行うには「何をどう処理すればそれを行うことができるのか」という処理アルゴリズムを考えることがソフトウェアを作る際に最も大切な事です。いわゆる設計ですね。

ソースコードを書いて、コンパイルリンクをして、代表的な正常データを与えると正しい結果が得られるというデバッグまでが製造です。

その後には各種の正常ケースに加え、異常データや異常ケースを含めてソースコードに表した全てのロジックを通すための試験の項目を設計し、そのためのデータを用意して試験を行います。

で。大きなソフトウェアになると設計も試験も複数フェーズにわかれ各段階ごとにやるべきことが決まっている・・・という形になります。

参考まで。
    • good
    • 0
この回答へのお礼

なるほど...
今まではC言語が必修の授業で,課題をただこなすだけだったのですが,
これから学校の実験や,将来的には仕事などでもプログラミングを使うことになると思うので,どのように処理すればよいか,きちんと考えようと思います.
何回も回答していただき本当にありがとうございます.

お礼日時:2020/08/01 18:01

No.1です。



追加です。
答えは一つではありません。
処理手順は一つではないということです。
ただ、他の方法と比較して明らかに手順が多かったり、複雑であったりする方法はNGです。

ちなみに勤めていた会社の研修担当として受講者のソースコードを読んだ経験がありますが、いろいろ読むと「この人はあの人の物を写した(or教えてもらった)な」とか「この人のがオリジナルで他の人はそれをまねた(or教えてもらった)な」といったことはすぐにわかります。(^^;
変数名を変えたりメッセージを変えたりしても。。。

ということで自分自身でしっかり考えて「私はこうだ」という処理手順を作れるようになりましょう。独創的である必要は全くありませんから。
    • good
    • 0
この回答へのお礼

そうなのですね...
今までわからないものはブログなどを見て書いていたので,もう一度やり直してみようと思います.
いろいろと教えてくださって本当にありがとうございます.

お礼日時:2020/08/01 16:09

うーむ・・・。


これって宿題なのかしらん。

いや、もし、実用的にこういうプログラムが書きたい、ってのならC言語じゃない方が良いでしょう。と言うか、恐らく「やりたいこと」をマトモにやるとしたら、100人中96人くらいはC言語の使用を避けるんじゃないかなぁ。

ぶっちゃけ、C言語には文字列がありません。文字列が無い言語で文字列操作をしようとするのに土台無理があります(笑)。文字列を操作する為には「素のC言語」から何個ものレイヤーを重ねてる、ってのが「実用性」で言うと現実です。
例えばC言語が生まれたUNIXでも、素のCでテキストファイルの中の文字列を操作するのを避けさせる為に、sedやらawk等のスクリプト言語が生まれました(ある意味Perlの遠い祖先です)。それくらい文字列操作を直接Cでやるのはクッソメンド臭いのです。間違いなく死にます(謎
いや、何かCで上手いことやれるライブラリ無いのかしらん、って探してみたんですが、見つからなかったんですよね。Windowsを使ってるんだろうし、強力なデータ構造がてんこ盛りのGLibとか、一応調べてみたんですが、多分この辺使うのは難しいでしょうねぇ。

さて、と。取り敢えず前提から。
まずはコピー先の文字配列ts[15]の問題から行きましょう。まずこれが実は問題に合いません。
どういう事かと言うと、他のスクリプト言語、と言われる言語での実行例をお見せします。

# Python での例

>>> s = "0.2\t0.0\t1.0"
>>> s.split("\t")
['0.2', '0.0', '1.0']
>>>

;; Scheme での例

#;1> (import srfi-13) ;; 文字列ライブラリを使用
; loading /var/lib//chicken/11/srfi-13.import.so ...
; loading /var/lib//chicken/11/chicken.fixnum.import.so ...
; loading /var/lib//chicken/11/chicken.platform.import.so ...
; loading /var/lib//chicken/11/srfi-14.import.so ...

Note: re-importing already imported identifier: string->list

Note: re-importing already imported identifier: string-copy

Note: re-importing already imported identifier: string-fill!
#;39> (define s "0.2\t0.0\t1.0")
#;70> (string-tokenize s)
("0.2" "0.0" "1.0")

さて、実は元々、問題としては、これは

> 実数値の文字列を切り出し

と言っていましたが、実は実数値の文字列もクソも関係ないんです。そうじゃなくって、逆に「(タブ等の)区切り文字部分で文字列を分割せよ」と言うのが主題です。こういう作業を「トークンに分ける作業」、すなわち「トーカナイズ」等と呼んだりします。主役は実は「区切り文字(タブの他に例えばスペース、コンマ、ピリオド等を利用する)」の方なんですね。
んで、上で利用したスクリプト言語なんてのは、見て分かる通り、このトーカナイズをやすやすとやってのける機能を持ってるんですが、結果を見て下さい。両者とも文字列を要素に持つリスト(C言語で言う配列みたいなモノ)を返しています。
つまり、このテの機能をC言語でやる場合、「文字配列の配列」を返した方が適する、と言う事が予想出来ます。
と言う事は、最初に挙げた質問で考えると、

ts[15]

ではダメで、

ts[3][5]

にせなアカン、と言う事です。二次元配列ですね。それを使わないといけません。

さて、もう一つは、一応、C言語の標準ライブラリにトーカナイズが出来るライブラリが(貧弱ながらも)含まれています。それはstring.hと言うヘッダなんですが、詳しくはこちらの方を参考にしてください。

C言語関数辞典 string.h:
http://www.c-tipsref.com/reference/string.html

このサイトはCの標準ライブラリを調べる際にはかなり重宝します。なんか分からん時はここで調べましょう。
この内の次の2つの関数を使用します。

strtok(文字列を区切り文字で分割する): http://www.c-tipsref.com/reference/string/strtok …
strcpy(文字列をコピーする): http://www.c-tipsref.com/reference/string/strcpy …

この2つを利用して書くと以下のようになりますね。

#include <string.h>

int main(void)
{
 char s[40] = "0.2\t0.0\t1.0\0";
 char ts[3][5];
 char* temp;
 int i = 1;

 temp = strtok(s, "\t");
 strcpy(ts[0], temp);
 while (temp != NULL) {
  temp = strtok(NULL, "\t");
  if (temp != NULL) {
   strcpy(ts[i], temp);
  }
  i++;
 }
 return 0;
}

特に出力は噛ましてませんが、「何のファイルを読むのか」具体例もありませんし、改造する為のベースならこれで充分じゃないか、と思います。出力自体も書き足すには苦労しないでしょうしね。

まぁでも、くれぐれも「素のC言語」で、文字列操作の為の実用プログラムを書こう、なんて思わないでください(笑)。世の中にはもっと便利なツールがあるのでそっちを利用しましょう(笑)。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます.
学校の課題になっております.
他の言語のほうが使い勝手が良いのですね...
もうすぐ夏休みなので自分で勉強しようと思います.
いろいろ例を書いていただき,ありがとうございました!

お礼日時:2020/08/01 16:05

使い勝手の悪さに定評のある strtok という手もないわけではない, かもしれない. 「あんなの使うくらいなら自力で全部頑張る」という人の方が多いかもしれないけど.



あと, 画像で貼られても読めない.
    • good
    • 1
この回答へのお礼

画像の件についてですがすみませんでした...
次からは直接入力するようにします.

お礼日時:2020/08/01 16:02

> しかし,2つ目と3つ目の実数値を取り出す際,どのように書けるかがわかりません.



1つ目のTABを検出したところでforループを抜けるので、その後ろにiで位置付いているTAB文字の次の字から同じようにチェックを続ければよい・・・とは考えませんか?

そう考えると「そもそもTAB文字を検出したところでループ処理を抜けてよいのか?」とはなりませんか?
つまり「入力された文字列の最初から最後までをチェックしTAB文字は読み飛ばして・・・」という処理にすればよさそう・・・とか考えませんか?

そういった部分がアルゴリズムを考えることかと。
小学校ではじまったプログラミング学習に狙うところですね。

参考まで。
    • good
    • 0
この回答へのお礼

ご回答ありがとうございます.
補足のほうで画像を貼らせていただいたのですが,こんな解釈でいいのでしょうか.
お返事いただけたら幸いです.

お礼日時:2020/07/31 21:54

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!