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

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

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

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

4回答

1052閲覧

C# リストからの抽出

Melvin

総合スコア13

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

2クリップ

投稿2022/07/10 09:54

前提

初心者です。

csvファイルから英単語、日本語、発音記号を読み込んで、以下のようにコンソール画面に出力したいです。

fabricate
(…を)組み立てて製造する
[fˈæbrɪkèɪt]
revoke
取り消す、廃止する
[rɪvóʊk, rɪvˈəʊk]
violate
犯す、破る
[vάɪəlèɪt, ˈvaɪʌleɪt]

csvファイルは以下のようになっています。

csv

1fabricate,(…を)組み立てて製造する,[fˈæbrɪkèɪt] 2revoke,取り消す、廃止する,[rɪvóʊk, rɪvˈəʊk] 3violate,犯す、破る,[vάɪəlèɪt, ˈvaɪʌleɪt]

試したC#コード

C#

1using System; 2using System.Collections.Generic; 3using System.IO; 4using System.Linq; 5 6internal class Program 7{ 8 static void Main(string[] args) 9 { 10 StreamReader sr = new StreamReader(@"読み込むcsvファイルのパス"); 11 12 var lis1 = new List<string>(); 13 while (!sr.EndOfStream) 14 { 15 string line = sr.ReadLine(); 16 lis1.Add(line); 17 } 18 19 string split = String.Join(",", lis1); 20 Console.WriteLine(split); 21 22 var lis2 = new List<string>(); 23 lis2 = split.Split(',').ToList(); 24 25 for (int i = 0; i < lis2.Count; i += 3) 26 { 27 Console.WriteLine(lis2[i]); //英語 28 Console.WriteLine(lis2[i+1]); //日本語 29 Console.WriteLine(lis2[i+2]); //発音記号 30 } 31 } 32} 33

発生している問題

以下、出力結果。

fabricate
(…を)組み立てて製造する
[f?abr?ke?t]
revoke
取り消す、廃止する
"[r?vo?k
r?v???k]"
violate
犯す、破る
"[v???le?t
?va??le?t]"

・発音記号に含まれる" , "によって英語、日本語、発音記号がうまく分離できない。
・発音記号が文字化けしている。

知りたいこと

・プログラム中の" lis2 "のリストを作ったうえで、前提に書いたように出力したいです。
・文字化けの対処法が知りたいです。
よろしくお願いいたします。

補足情報

visual studio 2022

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

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

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

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

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

BeatStar

2022/07/10 10:39 編集

OSはWindowsでいいですか? それと、ファイルの文字コードは何ですか? もしかするとファイルの文字コードの問題か、OSの設定によるものかもしれません。 ヒント: https://otona-life.com/2022/06/10/121615/amp/
Zuishin

2022/07/10 10:46 編集

カンマが余計なところに含まれているのは CSV として正しくないので、修正が必要です。 abc,def を二つに分けたくないなら、"abc,def" としなければなりません。 当然これは Split によって単純に分けることはできないので、ダブルクォーテーションで囲まれたところを除いて分割するようなプログラムにしなければなりません。 文字化けするのは、コンソールその他もろもろが Unicode 対応していないからでしょう。 対応させてください。
Melvin

2022/07/10 10:44

windowsです。 文字コードはメモ帳で確認したところ、UTF-8(BOM付き)でした。
KOZ6.0

2022/07/10 10:53

csv を保存するときのコードは何ですか?BOMはありますか?
KOZ6.0

2022/07/10 10:54

しまった、リロードしたら回答がありましたね
BeatStar

2022/07/10 11:34

ボムありの方でやってみてください。もしそれでできないならOS側の設定かも?
退会済みユーザー

退会済みユーザー

2022/07/11 13:44

質問者さん、無言ですが、回答されているのでそれらに対するフィードバックを返してください。役に立った/立たなかったぐらいはすぐに返せるのでは? 役に立たなかったならどこがダメかを書くとより期待に近い回答が出てくるかも。とにかく無言は NG です。
Melvin

2022/07/11 13:54

返信が遅れて申し訳ありません。 文字化けですが、回答いただいたように実行しましたら、解消しました。また、発音記号の分離ですが、csvを書き換えることができる仕様でしたので、書き換えることで解決しました。 回答していただいた皆様、ありがとうございました。
guest

回答4

0

発音記号にカンマを含んでいますが、最後の項目なので、1行ずつ処理して3番目の項目以降はくっつければ良いです。
(途中の項目だとそうはいきません。)

出力時のエンコーディングは Console.OutputEncoding で決まります。
Unicode 系なら何でも良いですが、ファイルにリダイレクトしたときを考えて選択するといいと思います。
メモ帳の対応を考えると UTF8 が無難な気がします。

C#

1using System; 2using System.Collections.Generic; 3using System.IO; 4using System.Text; 5 6class Program 7{ 8 static void Main(string[] args) { 9 StreamReader sr = new StreamReader(@"test.csv", Encoding.UTF8); 10 Console.OutputEncoding = Encoding.UTF8; 11 12 var lis1 = new List<string>(); 13 while (!sr.EndOfStream) { 14 string line = sr.ReadLine(); 15 lis1.Add(line); 16 } 17 18 string split = String.Join(",", lis1); 19 Console.WriteLine(split); 20 21 var lis2 = new List<string>(); 22 foreach (var line in lis1) { 23 var fields = line.Split(','); 24 lis2.Add(fields[0]); 25 lis2.Add(fields[1]); 26 lis2.Add(string.Join(",", fields, 2, fields.Length - 2)); 27 } 28 29 for (int i = 0; i < lis2.Count; i += 3) { 30 Console.WriteLine(lis2[i]); //英語 31 Console.WriteLine(lis2[i + 1]); //日本語 32 Console.WriteLine(lis2[i + 2]); //発音記号 33 } 34 } 35}

投稿2022/07/10 11:29

KOZ6.0

総合スコア2626

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

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

0

ベストアンサー

小細工はしないで正攻法でやることをお勧めします。例えば、

(1) CSV のフィールドにカンマが含まれる場合、フィールドをダブルクォート(")で囲む。

以下の記事の「CSV形式とは?」を読んでください。

CSV形式のファイルをDataTableや配列等として取得する
http://dobon.net/vb/dotnet/file/readcsvfile.html

(2) 文字コードをきちんと考える

今は CSV ファイルは Shift_JIS になっているとか? だとすると、Shift_JIS に無い文字が使われているので、ファイルの中身が既に文字化け状態です。

文字化けしているのを StreamReader が読んでくるので、そのまま文字化けで出力されるというのが現状ではないのですか?


【追記】

上に書いた (1), (2) に沿った例を追記しておきます。

(1) CSV のフィールドにカンマが含まれる場合、フィールドをダブルクォート(")で囲む。

以下の画像のように [rɪvóʊk, rɪvˈəʊk] と [vάɪəlèɪt, ˈvaɪʌleɪt] を " で囲います。文字コードは BOM なしの UTF-8 にしておきます(メモ帳のデフォルト)。

イメージ説明

(2) 文字コードをきちんと考える

アプリで CSV ファイルを読むときは UTF-8 で、コンソールに出力する時はデフォルトの CodePage ではなく Unicode とします (CodePage = 日本語 OS では Shift_JIS に含まれない文字があるので)。

コンソールの Unicode サポート
https://docs.microsoft.com/ja-jp/dotnet/api/system.console?view=net-6.0#unicode-support-for-the-console

サンプルコードを以下に載せておきます。.NET 6.0 のコンソールアプリです。上の (1) のような CSV ファイルの処置が可能なパーサーを自力でコーディングするのは簡単ではありませんので、既存のパーサー TextFieldParser を使っています。

TextFieldParser クラス
https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.visualbasic.fileio.textfieldparser?view=net-6.0

using Microsoft.VisualBasic.FileIO; using System.Text; string path = @"C:\Users\surfe\Documents\Visual Studio 2022\Net6App\ConsoleAppCsv\ConsoleAppCsv\CsvFile1.csv"; // CSV ファイルが UTF-8 なので第 2 引数に Encoding.UTF8 を設定 using (var parser = new TextFieldParser(path, Encoding.UTF8)) { var list = new List<List<string>>(); //フィールドがデリミタで区切られている parser.TextFieldType = FieldType.Delimited; // デリミタを , とする parser.Delimiters = new string[] { "," }; // フィールドを " で囲み、改行文字、デリミタを含めることができるか parser.HasFieldsEnclosedInQuotes = true; // フィールドの前後からスペースを削除 parser.TrimWhiteSpace = true; while (!parser.EndOfData) { string[]? fields = parser.ReadFields(); if (fields != null) { list.Add(fields.ToList()); } } // コンソールに出力する時はデフォルトの CodePage ではなく Unicode // とします (CodePage = 日本語 OS では Shift_JIS に含まれない文字 // があるので) Console.OutputEncoding = Encoding.Unicode; foreach (List<string> line in list) { foreach (string field in line) { Console.WriteLine(field); } } }

実行結果は以下の通りです。

イメージ説明

投稿2022/07/11 00:36

編集2022/07/11 02:01
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

発音記号に含まれる" , "によって英語、日本語、発音記号がうまく分離できない

まず、コードを読んでみましょう。
コードを読むコツは「一行レベルで、その行が何をしているか考えながら読む」です。

C#

1Internal class Program 2{ 3 static void Main(string[] args) 4 { 5 // ファイルを読み込むためのオブジェクトを生成 6 StreamReader sr = new StreamReader(@"読み込むcsvファイルのパス"); 7 // lis1をstring用のListとして生成 8 var lis1 = new List<string>(); 9 // ファイルが最後尾まで以下を繰り返す 10 while (!sr.EndOfStream) 11 { 12 // 一行レベルで読み取り、 13 string line = sr.ReadLine(); 14 // lis1に追加 15 lis1.Add(line); 16 } 17 // lis1のデータを”,”を区切り文字として文字列にし、表示 18 string split = String.Join(",", lis1); 19 Console.WriteLine(split); 20 21 // string用のListを生成し、 22 var lis2 = new List<string>(); 23 // 先ほど連結した文字列を再び分割 24 lis2 = split.Split(',').ToList(); 25 26 // 3ごとに以下をループ 27 for (int i = 0; i < lis2.Count; i += 3) 28 { 29 // それぞれ出力 30 Console.WriteLine(lis2[i]); //英語 31 Console.WriteLine(lis2[i+1]); //日本語 32 Console.WriteLine(lis2[i+2]); //発音記号 33 } 34 } 35}

みたいな感じですね。

で、これを実際にデータを使ってやってみましょう。

まずファイルを読み込むとlis1には

lis1[0] = “fabricate,(…を)組み立てて製造する,[fˈæbrɪkèɪt]” lis1[1] = “revoke,取り消す、廃止する,[rɪvóʊk, rɪvˈəʊk]” lis1[2] = “violate,犯す、破る,[vάɪəlèɪt, ˈvaɪʌleɪt]”

となっているはずです。

そしてそれをString.Joinで連結し、

split = “fabricate,(…を)組み立てて製造する,[fˈæbrɪkèɪt],revoke,取り消す、廃止する,[rɪvóʊk, rɪvˈəʊk],violate,犯す、破る,[vάɪəlèɪt, ˈvaɪʌleɪt]”

となりますね。
そしてそれを分割すると。

分割するとき、先端から分割していくので、区切り文字が出てくる最初の場所は、fabricateの直後。そのため、fからeまでを取り出して一つの文字列として認識。
先端から区切り文字までは無いものとして次の区切り文字までを一つとする。すると”(“から「する」までを一つとする。次に出てくる区切り文字を探すと、fabricateの発音記号の部分まで一つとして切り出すことに。
で、またさらに区切り文字までを一つとしてrevokeを取り出す。…とやりますが、
revokeの発音記号の場所に区切り文字が
含まれているのでそこまでを一つとする。
次はそこから開始して…となるので、

lis2[0] = fabricate lis2[1] = (…を)組み立てて製造する lis2[2] = [fˈæbrɪkèɪt] lis2[3] = revoke Lis2[4] = 取り消す、廃止する Lis2[5] = [rɪvóʊk Lis2[6] = rɪvˈəʊk] lis2[7] = violate Lis2[8] = 犯す、破る Lis2[9] = [vάɪəlèɪt Lis2[10] = ˈvaɪʌleɪt]

となっているはずです。

…あれ? 本来は5番目と6番目は一つになっているべきですよね?
でもこれでは別々のものに。

区切り文字が発音記号の部分に含まれていますから当たり前です。

ではどうするか。対策としては、区切り文字としてのコンマ以外の方、つまり発音記号側に含まれている方のコンマを別の文字に置き換えておいて、読み込み後の分割後に置き換えるのです。
たとえば絶対にシャープ(#)が出てこない前提なら発音記号側の区切りをシャープに置き換えておく。
ファイルには、

fabricate,(…を)組み立てて製造する,[fˈæbrɪkèɪt] revoke,取り消す、廃止する,[rɪvóʊk# rɪvˈəʊk] violate,犯す、破る,[vάɪəlèɪt# ˈvaɪʌleɪt]

のような感じでやる。

そのファイルを読み込んで、文字列を分割し、シャープが含まれているのならシャープをコンマに置き換えればできるはずです。

ただし、このファイルに書くのがユーザの場合は問題が発生しやすいです。さらに仕様も変更しないといけないので自分で仕様を決める立場にないならこの方法は取れません。

どうしても仕様を質問にあるようなものにするならsplit処理をすでに提供されている方じゃなくて自前で実装しないといけないです。

投稿2022/07/10 11:30

BeatStar

総合スコア4958

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

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

Melvin

2022/07/11 13:57

丁寧に教えていただき、ありがとうございました。 とても分かり易かったです。
guest

0

カンマが入っている項目がうまく取れない問題については区切り文字をカンマ以外にすれば解決しますね。
区切り文字はカンマでなくてもいいのでとにかくデータにでてこないものを使うのが肝要です。
そもそもライブラリを使うのが簡単ですが。項目名で項目を引っ張れたりするライブラリが多くいろいろと便利です。
https://tech-and-investment.com/csv1/

投稿2022/07/10 11:07

usekay

総合スコア395

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問