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

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

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

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

Q&A

解決済

2回答

15090閲覧

C#を用いたエクセル操作(書式変換とエクセル解放)

hiro24

総合スコア12

C#

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

0グッド

0クリップ

投稿2018/07/28 01:09

いつもお世話になっております。

C#の初学者です。

この度は、C#を用いて以下の【目的の動作】を行うウィンドウズフォームアプリを作成する必要が出てきました。
そこで、作成している中で以下の【困っている点】で躓いてしまったため質問を投稿させていただきました。

【開発環境】
・OS:Windows 10
・Visual Studio 2017 ver.15.7.3
・.NET ver.4.7.02556

【目的の動作】
※C#を用いて、以下のエクセル操作を行う※
[1]文字列として保存されている数字を数値に変換したい
[2]文字列として保存されている日付を"yyyy/MM/dd"の日付の書式に変換したい
[3]%の書式で保存されている数値を標準の書式に変換する(例:5.0% ⇒ 0.05) ※もしくは%のみ取って5.0% ⇒ 5.0でも可
[4]ファイルをうまく読み込めなかった際にエクセルの解放の動作が正常に行われる

【困っている点】
[1]C手作業での書式変換のような動作方法をC#で行う方法が不明。
[2]以下のコードを実行すると「Insertできません」というダイアログが表示されて実行できない
[3]以下のコードを実行すると「%が消えない上に5%⇒500%」に変換されてしまう
[4]エラーがあるとプロセスが最後まで実行されずエクセルの開放が行われない

【変換対象】
イメージ説明

【コード】

public partial class Form1 : Form { public Form1() { } Microsoft.Office.Interop.Excel.Application xlApp = null; Microsoft.Office.Interop.Excel.Workbooks xlBooks = null; Microsoft.Office.Interop.Excel.Workbook xlBook = null; Microsoft.Office.Interop.Excel.Sheets xlSheets = null; Microsoft.Office.Interop.Excel.Worksheet xlSheet = null; Microsoft.Office.Interop.Excel.Range xlRange = null; Microsoft.Office.Interop.Excel.Range xlCells = null; private void button1_Click(object sender, EventArgs e) { openFileDialog1.ShowDialog(); if (System.IO.File.Exists(openFileDialog1.FileName)) { xlApp = new Microsoft.Office.Interop.Excel.Application(); xlBooks = xlApp.Workbooks; xlBook = xlBooks.Open(openFileDialog1.FileName); xlSheets = xlBook.Worksheets; xlSheet = xlSheets[1] as Microsoft.Office.Interop.Excel.Worksheet; //[1]convert string to int /*******************************************************************/                 問題点[1]のやり方が不明 /*******************************************************************/ //[2]の問題点 convert string to date xlCells = xlSheet.Cells; xlRange = xlCells[1, 2] as Microsoft.Office.Interop.Excel.Range; for (int i = 2; i <= xlSheets[1].UsedRange.Rows.Count; i++) { xlRange = xlCells[i, 2] as Microsoft.Office.Interop.Excel.Range; xlRange = xlRange.Insert(4,"/").Insert(7, "/"); } //[3]の問題点 xlRange = xlSheet.Range["C:C"]; xlRange.Replace("%", ""); xlBook.SaveAs(@"/*保存先*/"); //[4]の問題点 System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets); xlBook.Close(); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks); xlApp.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp); } else { MessageBox.Show("File does not exist. Or it is a non-compliant file."); } } }

お手数をおかけしてしまい申し訳ございませんが、どなたかお力添えの程頂けないでしょうか。

大変恐縮ですが、よろしくお願いいたします。

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

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

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

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

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

guest

回答2

0

ベストアンサー

Microsoft.Office.Interop.~と名前空間が長くて見にくいので、名前空間エイリアスを使用しましょう。

using ディレクティブ (C# リファレンス) | Microsoft Docs

[1]文字列として保存されている数字を数値に変換したい

普通に数値に変換して、セルに再代入すればOKです。

イメージ(VBA)

vba

1'[1]convert string to int 2 3'対象のセル範囲 4Dim toNumberRange As Excel.Range 5Set toNumberRange = xlApp.Intersect( _ 6 xlSheet.UsedRange, _ 7 xlSheet.Columns.Item("A") _ 8 ) 9 10'セル範囲の値配列(高速化用) 11 '速度度外視ならtoNumberRangeをforeachして、各セル直接編集でも可) 12Dim toNumValue() As Variant 'object[,] 13toNumValue = toNumberRange.Value 14 15Dim r As Long, c As Long 16For r = LBound(toNumValue, 1) To UBound(toNumValue, 1) 17 For c = LBound(toNumValue, 2) To UBound(toNumValue, 2) 18 'value is string and can convert to numeric 19 If VarType(toNumValue(r, c)) = vbString And _ 20 IsNumeric(toNumValue(r, c)) Then 21 22 toNumValue(r, c) = CDbl(toNumValue(r, c)) 'Convert string to double 23 End If 24 Next c 25Next r 26 27'再代入 28toNumberRange.Value = toNumValue

[2]文字列として保存されている日付を"yyyy/MM/dd"の日付の書式に変換したい

[1]と同じです。セルの値を日付に変換してセルに再代入しましょう。
今回は形式が固定のようなのでDateTime.TryParseExactあたりを使えば良いのではないでしょうか。

ところで、Range.Insert メソッド (Excel)はどんな処理か理解していますか?

[3]%の書式で保存されている数値を標準の書式に変換する

Range.NumberFormatLocal プロパティ (Excel)を設定すれば良いです。

リンク先のサンプルや、
ExcelのVBAのエディタのイミディエイトウィンドウを表示して
?ActiveCell.NumberFormatLocal
などで設定されている文字列がわかるので、「標準」になっているセルと同じ文字列を設定します。

[4]ファイルをうまく読み込めなかった際にエクセルの解放の動作が正常に行われる

landy77さんが回答されているようにtry catchを使用しましょう。

また全体で使わないのであればMarshal.FinalReleaseComObject メソッド (Object) (System.Runtime.InteropServices)の方が良いかもしれません。

ちょっと古い記事ですが以下も参考に

Office オートメーションで割り当てたオブジェクトを解放する – Part1 – Japan Office Developer Support Blog

投稿2018/07/28 02:47

imihito

総合スコア2166

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

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

hiro24

2018/08/01 13:56

ご回答ありがとうございました。 imihitoさんのご助言のおかげで、[1], [3], [4]の問題は解決することができました。 しかし、[2]の日付の変換を以下のように試したのですが自分の力不足で上手く実行できませんでした。 お手数をおかけしてしまい申し訳ございませんが、どこが悪いかご助言いただけないでしょうか。 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; using System.Globalization; namespace WindowsFormsApp1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { } Microsoft.Office.Interop.Excel.Application xlApp = null; Microsoft.Office.Interop.Excel.Workbooks xlBooks = null; Microsoft.Office.Interop.Excel.Workbook xlBook = null; Microsoft.Office.Interop.Excel.Sheets xlSheets = null; Microsoft.Office.Interop.Excel.Worksheet xlSheet = null; Microsoft.Office.Interop.Excel.Range xlRange = null; Microsoft.Office.Interop.Excel.Range xlCells = null; private void button1_Click(object sender, EventArgs e) { openFileDialog1.ShowDialog(); CultureInfo jaJP = new CultureInfo("ja-JP"); DateTime dateValue; try { if (System.IO.File.Exists(openFileDialog1.FileName)) { xlApp = new Microsoft.Office.Interop.Excel.Application(); xlBooks = xlApp.Workbooks; xlBook = xlBooks.Open(openFileDialog1.FileName); xlSheets = xlBook.Worksheets; xlSheet = xlSheets[1] as Microsoft.Office.Interop.Excel.Worksheet; //[1]convert string to int(解決できました) xlRange = xlSheet.Range["A:A"]; xlRange.NumberFormat = "General"; //[2]の問題点 convert string to date(未解決)※実行すると例外へ飛んでしまう。 xlCells = xlSheet.Cells; xlRange = xlCells[1, 2] as Microsoft.Office.Interop.Excel.Range; for (int i = 2; i <= xlSheets[1].UsedRange.Rows.Count; i++) { xlRange = xlCells[i, 2] as Microsoft.Office.Interop.Excel.Range; if (DateTime.TryParseExact(xlRange.Value, "yyyyMMdd", jaJP, DateTimeStyles.None, out dateValue)) { xlRange.Value = dateValue; } } //[3]の問題点(解決できました) xlRange = xlSheet.Range["C:C"]; xlRange.NumberFormat = "General"; xlBook.SaveAs(/**/); } } catch { MessageBox.Show("File does not exist. Or it is a non-compliant file."); } finally { //[4]の問題点 (解決出来ました) System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheet); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlSheets); xlBook.Close(); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBook); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlBooks); xlApp.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(xlApp); } } } } 申し訳ございませんが、今一度お力添えの程お願いいたします。 よろしくお願いいたします。
imihito

2018/08/01 14:20

例外の情報を握りつぶしてしまっていますが、具体的にどのような例外が発生したか確認されたでしょうか? 恐らく`.ToOADate()`あたりで解決するとエスパーしますが、もう少し情報が無いと難しいです
hiro24

2018/08/01 14:35

ご回答ありがとうございます。 例外の情報記載が漏れてしまい申し訳ございません。 例外動作ですが、以下のようになります。 bottonを押すところまでは、実行できています。 catch { MessageBox.Show("File does not exist. Or it is a non-compliant file."); } が実行されて上記のメッセージボックスが表示されて終了する動作になっています。 それにより、エクセルの中身を変更する動作が何も実行されず(※[4]は実行されました。)終了になっています。 ちなみに、[1], [3], [4]はこの[2]関連の部分を削除した状態で実行して正常に動作していることは確認しました。 現状では、このようになっています。
imihito

2018/08/01 14:47

それはコードを見ればわかる話です 知りたいのは実行時にしかわからない情報、実際に発生した例外の情報です そのcatchで握りつぶした例外の情報(例外の種類・メッセージ・発生行など)をちゃんと確認したかどうかを聞いています
hiro24

2018/08/02 13:42

勘違いしてしまい申し訳ございません。 63行目に関して以下の警告が出ました。 if (DateTime.TryParseExact(xlRange.Value, "yyyyMMdd", jaJP, DateTimeStyles.None, out dateValue))←63行目のコード 'System.DateTime.TryParseExact(string, string, SystemIFormatProvider, System.Globalization.DateTimeStyles, out System.DateTime)'に最も適しているオーバーロードメソッドに無効な引数がいくつか含まれています。 こちらのような警告が出ておりました。
imihito

2018/08/02 14:11

そもそもメソッドに渡しているものがおかしい、ということですね。 恐らくデータ(xlRange.Value)の中に数値が含まれていると思われます。 明確に文字列であることを確認してから渡す、ということで手は色々あると思いますが `xlRange.Value`では無く、`xlRange.Text`でセルに表示されている文字列を取得してTryParseExactに渡すのが一番手軽だと思われます。
hiro24

2018/08/02 14:33

迅速なご回答ありがとうございました。 ご指摘いただいたとおりに修正いたしましたら、無事に実行できました。 【修正箇所抜粋】 for (int i = 2; i <= xlSheets[1].UsedRange.Rows.Count; i++) { xlRange = xlCells[i, 2] as Microsoft.Office.Interop.Excel.Range; if (DateTime.TryParseExact(xlRange.Text, "yyyyMMdd", jaJP, DateTimeStyles.None, out dateValue)) { xlRange.Value = dateValue; } } .Valueは、数値型だったのですね。自分はstring型だと勘違いしておりどうして上記の例外が出てくるのかわからないでいました。 大変勉強になりました。ありがとうございました。 また質問をさせていただく機会もあると思いますが、その時は何卒よろしくお願いいたします。
imihito

2018/08/02 14:54

Excelの`Range.Value`は非常に多くの種類の値を返します。 ざっと思いつくだけでも以下の値が返される可能性があります。 String・Double・DateTime・Boolean・object[,]・Int32(エラー)・null
guest

0

コピペですか?
コピペでも良いですけど、それぞれの行が何をしてるのか理解していけば上の質問はいくつか出てこなくなるはずです。

解放の件は単純にエラー処理を全くしてないからです。
(正直問題外です)
try catch を調べて下さい。

エクセルの操作部分は「c# EXCEL 書式」で一杯出てくるでしょう。

ここら辺までやって「ここまでこうやってみたけどここが分からない。どうわからいのか?」を書いて初めて説明できると思います。

個人的に質問者さんがどういう理由で「必要が出てきた」のかは不明ですけど(答える側としてはどうでもいいですし)、エクセルの操作は全くの初心者の方には若干ハードルが高いと思います。
キャストの件とか一杯他にも知識が必要になりますし・・・・

とりあえず上に記述した所まではもう少し頑張ってみて下さい。

投稿2018/07/28 01:32

landy77

総合スコア1614

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問