質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

Q&A

解決済

3回答

1970閲覧

数値抜けtsv配列データの読み込み方法

uta06

総合スコア1

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

0グッド

0クリップ

投稿2021/08/14 07:18

編集2021/08/17 06:13

前提・実現したいこと

はじめまして
私はC言語でタブ区切りの数値データファイルの読み込みを以下のソースコードを使って読み込むことを試しております.その際,例えば以下のような4x4の配列データを読み込むとき,そのデータ(missing_value.tsv)に数値抜けがあった場合に適当な値(例えば0)などで補完して配列を読み込みたいと考えております(下記のデータ3行目は0 tab tab tab 3となっており, 1と2が抜けている状態です).

もしよい方法等ご存じでしたらご教授いただければ幸いです。
何卒よろしくお願い申し上げます.

read_tsv.cpp

C言語

1#include <stdio.h> 2 3int main(void){ 4 5 int i, j; 6 int x = 4, y = 4; 7 double A[x][y]; 8 9 //file open 10 FILE *file; 11 file = fopen("missing_value.tsv","r"); 12 if(file == NULL){ 13 printf("File not opened\n"); 14 return 0; 15 } 16 17 //read data 18 for(i=0; i<x; i++){ 19 for(j=0; j<y; j++){ 20 fscanf(file, "%lf", &(A[i][j])); 21 } 22 } 23 24 //file close 25 fclose(file); 26 27 //display data 28 for(i=0; i<4; i++){ 29 for(j=0; j<4; j++){ 30 printf("%lf ", A[i][j]); 31 } 32 printf("\n"); 33 } 34 return 0; 35 36}

missing_value.tsv

C言語

10 1 2 3 20 1 2 3 30 3 40 1 2 3

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kazuma-s

2021/08/14 11:27 編集

> 下記のデータ3行目は0, tab, tab, 3となっており, 3行目は「0 tab tab tab 3 改行」ではありませんか? 1行目が「0 tab 1 tab 2 tab 3 改行」だとすると。
guest

回答3

0

scanf類の関数の変換指定子"%lf"では、
文字列先頭のホワイトスペース(空白やタブ文字等、isspace()関数に与えて1が返る文字)を全て読み飛ばし、ホワイトスペース以外の文字に到達したら、浮動小数点型として解釈できる文字列について変換し、浮動小数点型への変換に失敗したら(数字や.以外の文字が出てきたり文字列終端に到達したり)したら変換を終了、変換結果を与えられたポインタに格納して変換できた個数を返す
という処理をします(%dとかの整数入力、%sとかの文字列入力でも入力できる文字種が変わりますが手順は同様)。

で、水平タブはまさにこの「ホワイトスペース」ですので、タブや改行がいくつかある場合でも一気に読み飛ばされてしまいます。なので、"%lf"だけに頼っていては目的の処理はできません。
「よい方法」は、まず地道に処理を行えるようになってから考えましょう。

"%lf"だけに頼ってはできないので、「生」の入力文字列を取得して、自分で解析しましょう。
常套手段的には、fgets()で改行文字までを取得して、行内は'\t'を区切りに切り出して、strtod()で(成功/失敗を確認しながら)変換 を繰り返すことになるかと思います。いろいろバリエーションは考えられますが。

入力ファイルは、データが無いところでもtabが必ずはいっているのか、例えば
tabtab改行
1.23tab4.56tab改行
となっているのかそれともそういう行はtabが削られてしまって
改行
1.23tab4.56改行
になってしまったりするのか、によってはいろいろ気をつけなきゃいけないことが出てくるかもしれません。

投稿2021/08/15 00:31

編集2021/08/15 04:13
thkana

総合スコア7703

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2021/08/15 03:20

> 行内はstrtok()でタブ文字までを切り出して、 標準関数のstrtok()ですと、トークンの文字列が無いときに連続する区切り文字は1つの区切り文字として扱ってしまうので、この場合は残念ながら使えないと思います。 例:3と5の間が空 $ cat t1.c #include <stdio.h> #include <string.h> int main() { char s[256] = "1\t2\t3\t\t5"; char *p = strtok(s, "\t"); int n = 0; while (p != NULL) { ++n; printf("%d: p=[%s]\n", n, p); p = strtok(NULL, "\t"); } } $ gcc -Wall t1.c;./a.out 1: p=[1] 2: p=[2] 3: p=[3] 4: p=[5]
thkana

2021/08/15 04:11

> トークンの文字列が無いときに連続する区切り文字は1つの区切り文字として扱ってしまう でしたっけ...じゃあ、そこは「'\t'を探して」ぐらいの言い方の方がいいですかね。修正しておきます。御指摘ありがとうございます。 #C++のstringに標準でsplit関数があれば悩むこともないのでしょうが。
uta06

2021/08/16 14:57

thkana様,ご回答ありがとうございました. ご指摘のようにファイル読み込みに関する知識(ファイルストリームの仕組み等)が不足しておりました. 着実に一行,一文字ずつ分解&解読し,今後は安全な実装を務めてまいりたいと思います. 重ねてお礼申し上げます.
guest

0

ベストアンサー

区切り文字が '\t' または '\n' だとすると、
次のような読み込み方もあります。

C

1 for (i = 0; i < x; i++) { 2 for (j = 0; j < y; j++) { 3 A[i][j] = 0; 4 int c = fgetc(file); 5 if (c != '\t' && c != '\n') { 6 ungetc(c, file); 7 fscanf(file, "%lf%*c", &A[i][j]); 8 } 9 } 10 }

追記
別解

C

1#include <stdlib.h> // strtod 2 3 for (i = 0; i < x; i++) { 4 char s[256], *p = fgets(s, sizeof s, file); 5 for (j = 0; j < y; j++) { 6 A[i][j] = (*p=='\t'|| *p=='\n') ? 0 : strtod(p, &p); 7 p++; 8 } 9 }

投稿2021/08/14 21:59

編集2021/08/16 07:13
kazuma-s

総合スコア8224

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

uta06

2021/08/16 14:51

ご回答ありがとうございます. 1つ目のプログラムを参考に実装したところ,所望の処理をすることができました. 他の方からご指摘いただいたようにオーソドックスなのはfgets()で各行ごとに分解して見てゆく方法のようですが,kazuma-s様からご教授いただいた方法では私の当初のプログラムに近い形でかつ簡潔な実装であったため大変参考になりました. 重ねてお礼申し上げます.
guest

0

  • 1行分の文字列を読み込み
  • TABコードで文字列を分解する
  • それが数値であるなら数値変換、空白なら既定値を代入
  • 文字列が尽きるまで繰り返し
  • ファイルが尽きるまで繰り返し

というシーケンスでどうでしょう

投稿2021/08/14 07:33

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

uta06

2021/08/15 05:44

お早いご回答をありがとうございます. タブごとに分割し,地道に各個について処理するのがよさそうですね. 大変勉強になりました.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問