FC2ブログ
ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »
最近の記事

【C#】文字列 → float (浮動小数点) 変換でエラーが出るときは…  


 もう随分前の話になるが、フランス語圏の人から「VRM Live Viewer を使いたいのだが、起動ができない」というメッセージが来た。

 とりあえずアプリのバージョンと状況を聞いて確認してみたが、普通に起動できる。念の為、BOOTH にも出しているので DL して試したが、やはり同じ結果だ。

 しばらく何の事か全く見当が付かなかったのだが、根気よくスクショを送って貰ったり、動画を見せて貰ったりしているうちに、環境の違い(OS)くらいしかもう無いと意見が一致したので、彼は英語を、私はフランス語をインストールして OS を再起動してみた。

 そしたらどうだ。彼は「起動に成功した」と言い、私は彼が言っていたように起動に失敗した。

 原因は起動時に読み込まれる BVH ファイルで、そのファイルはテキストで書かれていて、小数点は "."(ドット) で書かれている。実はフランス語、アイスランド語など特定の言語圏では、小数点は "," (カンマ) で書くので、気の利いた OS (笑)がエラーを吐いていたのだ。

 かくして、ライブラリ作者に不具合報告をして、今ではそのフランスの VTuber もめでたく利用して貰っている。

特定の言語環境下において、BVHロードに失敗する

 まぁ、そんなこんなで「文字列を float (浮動小数点)に変換するなら、拡張メソッドを作っておいた方が良いよ」(※使うなら double 等も) という事で、簡単なものを紹介しておこう。



●フランス語、アイスランド語など特定の言語圏でも対応できる 文字列 → float (浮動小数点) 変換
using System;
using System.Globalization;

public static class Extensions //名前は任意
{
/// <summary>
/// 文字列 → float 変換 (CultureInfo.InvariantCulture 指定)
/// </summary>
/// <param name="s">変換元文字列</param>
/// <returns>変換後の float (失敗は 0)</returns>
public static float ToFloat(this string s)
{
if (float.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
{
return value;
}
return 0f;
}


/// <summary>
/// 文字列 → float 変換 (CultureInfo.InvariantCulture 指定)
/// </summary>
/// <param name="s">変換元文字列</param>
/// <param name="result">変換成功時 = float 値 / 変換失敗時 = 0</param>
/// <returns>true = 変換成功 / false = 変換失敗</returns>
public static bool TryParseFloat(this string s, out float result)
{
if (float.TryParse(s, NumberStyles.Number, CultureInfo.InvariantCulture, out var value))
{
result = value;
return true;
}
result = 0;
return false;
}
}

●メインコード例
using System;

var s = "1.234";
Console.WriteLine("ToFloat : " + s.ToFloat());

if (s.TryParseFloat(out var result))
{
Console.WriteLine("TryParseFloat : " + result);
}
else
{
Console.WriteLine("TryParseFloat failed");
}

ToFloat : 1.234
TryParseFloat : 1.234

 結果だけ見ると何て事ないのだが、この関数なら、フランス語、アイスランド語など特定の言語圏でもエラーにはならない。

 ちなみに、float 変換のオプションに使っている「CultureInfo.InvariantCulture」は「カルチャに依存しない」(無視する) ものらしい。要するに OS の言語とは関係なく、常に一定(英語圏)で変換されるようだ。むしろそっちの方がデフォだと有り難いのだが…(例えば文字列比較の Equals 等もデフォだとカルチャが効くので言語によって結果が異なる可能性がある)。

 必要なら double 等、他の浮動小数点も作っておいた方が良いだろう。

 動作確認するには OS にフランス語を入れて、OS の言語設定でフランス語に設定した後で、OS を再起動して確かめるしかないが、特に最近はアプリが世界中で使われるような時代になったので、これは常備した方が良いだろう。実際私もフランスの人から報告貰うまでは考えもしなかった。



 実は、不具合報告って開発者にとっては非常に有用な情報なので「なんか動作がおかしいな…」と思ったら、連絡してあげると良い。我々はバグ取りのエキスパートでもあるので、すぐに原因究明から修正案まで出せると思う。

 実際、この不具合(特定の言語環境下において、BVHロードに失敗する) はライブライリ元の UniVRM が抱えていた問題だったので、これが Fix されると、フランス圏の人もアプリや VRM が利用できるようになったわけだしね(少なくとも BVH を使うアプリはそれまでフランス圏では利用できなかったハズ:3tene とかも BVH 機能入ってた気がする)。

 単純に不具合が直ると自分も幸せだし、他の多くの利用者も幸せになれるしね(笑)。





(関連記事)
【Unity】【C#】StartsWith, EndsWith が遅いのなら、Equals も遅いのか?
【Unity】【C#】範囲を指定できる Mathf.Repeat
【C#】【Unity】enum 型と string, int 型の相互変換など
【Unity】色形式:Unity の Color と Android の ARGB(int32) の相互変換をする


関連記事
スポンサーサイト



category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  算術関数 
tb: 0   cm: --

【C#】配列やコレクションの IsNullOrEmpty  


 ああ、そう言えば「配列 や List の TryGetValue」やったのに、もっとよく使うもの忘れてたな~、と思い出したので、とても簡単なものだが、あると非常に便利ものなので一応書いておこう。

 これは文字列で string.IsNullOrEmpy() をよく使うので、同じような感覚で、配列や List 等が null または 空 (Count = 0) でないかを調べるものがあれば、便利なのにな~と思ったので、昔作ったものだ。実際にあまりに頻繁に使うので、標準関数かと思い違いしてしまうほどだ(笑)。

 まぁたぶん、皆似たようなものを作ってる気がするが、まだ勉強しはじめたばかりの人もいるかもなので(面白いことに、もう随分たくさんの記事を書いたが、上級者向けより、初心者向けの記事の方がアクセスは多いものなのだ)、役に立つかはわからないが、とりあえず書いておこう。



●配列やコレクションの IsNullOrEmpty
using System;
using System.Collections.Generic;

public static class Extensions //名前は任意
{
/// <summary>
/// コレクションが null または Count = 0 か?
/// 2021/07/11 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-399.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="collection">調べるコレクション</param>
/// <returns>true : null または Count = 0</returns>
public static bool IsNullOrEmpty<T>(this ICollection<T> collection)
{
return collection == null || collection.Count == 0;
}

/// <summary>
/// 配列が null または Length = 0 か?
/// 2021/07/11 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-399.html
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="array">調べる配列</param>
/// <returns>true : null または Length = 0</returns>
public static bool IsNullOrEmpty<T>(this T[] array)
{
return (array == null || array.Length == 0);
}
}

●メインコード例
using System;
using System.Collections.Generic;

var emptyList = new List();
var list = new List() { 1, 2, 3 };
Console.WriteLine("emptyList : " + emptyList.IsNullOrEmpty());
Console.WriteLine("list : " + list.IsNullOrEmpty());

var emptyDic = new Dictionary();
var dic = new Dictionary()
{
{ "hoge", 100 },
{ "fuga", 200 },
};
Console.WriteLine("emptyDic : " + emptyDic.IsNullOrEmpty());
Console.WriteLine("dic : " + dic.IsNullOrEmpty());

var emptySet = new HashSet();
var set = new HashSet() { "hoge", "fuga" };
Console.WriteLine("emptySet : " + emptySet.IsNullOrEmpty());
Console.WriteLine("set : " + set.IsNullOrEmpty());

var emptyArray = new string[0];
var array = new string[] { "apple", "banana", "candy" };
Console.WriteLine("emptyArray : " + emptyArray.IsNullOrEmpty());
Console.WriteLine("array : " + array.IsNullOrEmpty());

emptyList : True
list : False
emptyDic : True
dic : False
emptySet : True
set : False
emptyArray : True
array : False

 これも本当になんてことないものなんだけどね。できればこれも標準関数に欲しいくらい(笑)。

 私はこれに NOT値(反転値)を求める IsExist() みたいな定義も加えて、よく使っている。

●!IsNullOrEmpty() (反転値) を返す IsExist()
//null でない、かつ Count > 0
public static bool IsExist(this ICollection collection) => !collection.IsNullOrEmpty();

//null でない、かつ Length > 0
public static bool IsExist(this T[] array) => !array.IsNullOrEmpty();

 これは「if (!list.IsNullOrEmpty())」みたいなものを「if (list.IsExist())」と「!」を入れないで済むので、少しわかりやすくなるので重宝してる。まぁ、日本語でも「〇〇でなくはない」みたいな否定の否定って意味がわかりずらいからね。その辺は好みで(笑)。






(関連記事)
【C#】配列 や List の TryGetValue
【Unity】【C#】配列・リストのシャッフル
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump


関連記事

category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  配列操作  連想配列 
tb: 0   cm: --

【C#】HashSet や Dictionary の AddRange  


 前回に引き続き「標準関数にあったら良いな」シリーズ(笑)。

 List には後から、要素を複数追加する AddRange() というメソッドがあるが、残念ながら、HashSetDictionary には無い。しかし、意外と複数の要素を追加したいときってあるんだよね。そんな時に便利な拡張メソッド。



●HashSet, Dictionary の AddRange
using System;
using System.Collections.Generic;

public static class Extensions //名前は任意
{
/// <summary>
/// 複数の要素を追加する(同じ値は上書きとなる)
/// ※null であっても、必ず生成される
/// 2021/06/13 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-398.html
/// </summary>
/// <param name="set">追加先</param>
/// <param name="collection">追加ソース</param>
/// <returns></returns>
public static HashSet<T> AddRange<T>(this HashSet<T> set, IEnumerable<T> collection)
{
if (set == null)
set = new HashSet<T>();

if (collection != null)
{
foreach (var item in collection)
set.Add(item);
}
return set;
}

/// <summary>
/// 複数のキーと値ペアを追加する(同じキーは上書きとなる)
/// ※null であっても、必ず生成される.
/// 2021/06/13 Fantom
/// http://fantom1x.blog130.fc2.com/blog-entry-398.html
/// </summary>
/// <param name="dst">追加先</param>
/// <param name="src">追加ソース</param>
/// <returns></returns>
public static Dictionary<K, V> AddRange<K, V>(this Dictionary<K, V> dst, Dictionary<K, V> src)
{
if (dst == null)
dst = new Dictionary<K, V>();

if (src != null)
{
foreach (var item in src)
dst[item.Key] = item.Value;
}
return dst;
}
}

●メインコード例
using System;
using System.Collections.Generic;
using System.Linq;

var aSet = new HashSet<int>(Enumerable.Range(0, 5));
Console.WriteLine("Before : " + aSet.Dump());

var bSet = new HashSet<int>(Enumerable.Range(10, 5));
aSet.AddRange(bSet);
Console.WriteLine("After : " + aSet.Dump());

var cSet = new HashSet<int>(aSet).AddRange(bSet);
Console.WriteLine("c = a + b : " + cSet.Dump());

var aDic = new Dictionary<int, string>()
{
{ 0, "a" },
{ 1, "b" },
{ 2, "c" },
};
Console.WriteLine("Before : " + aDic.Dump());

var bDic = new Dictionary<int, string>()
{
{ 10, "x" },
{ 11, "y" },
{ 12, "z" },
};
aDic.AddRange(bDic);
Console.WriteLine("After : " + aDic.Dump());

var cDic = new Dictionary<int, string>(aDic).AddRange(bDic);
Console.WriteLine("c = a + b : " + cDic.Dump());

Before : [0, 1, 2, 3, 4]
After : [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
c = a + b : [0, 1, 2, 3, 4, 10, 11, 12, 13, 14]
Before : [[0, a], [1, b], [2, c]]
After : [[0, a], [1, b], [2, c], [10, x], [11, y], [12, z]]
c = a + b : [[0, a], [1, b], [2, c], [10, x], [11, y], [12, z]]

 要素をダンプしている Dump() は以前の記事からコピペして欲しい。

 少し標準関数の List.AddRange() と違うのは、void を返してるのではなく、コレクションを返してる所かな。こうすると、メソッドチェーンみたいにできるので、new と同時にいくつかの別コレクションを追加できたりするので、便利だ。

 とても簡単なものだけど、これもあると非常に有用なんだよね。私は結構大きなシステムを自作してるけど、大きくなるほど、こういった便利関数は欠かせなくなる(毎回似たような処理を書くのは大変な物量になるため)。実はプログラミングってこういう小さな工夫の積み重ねなんだよね(笑)。



 「これくらいのものなら、LINQ で書けばいいんじゃないの?」と思うかも知れないが、私はあまりオススメしない。というのは LINQ はそれなりにコストがかかるのと、GC を発生しやすいので、頻繁に使われるかも知れない拡張メソッド等は、割とベタな書き方をした方が速いし、GC も制御しやすいからだ。まぁ、string 系の処理はどのみち GC が発生するので使うこともあるが(しかし、できる限り StringBuilder を使う方が良い)、LINQ はどちらかというと、ランタイムで1回きりの初期化処理とか、頻繁には使わない処理用かな。特に Unity のような毎フレーム更新するような処理には気をつけて使った方が良いね。

コストのかかる操作を避ける





(関連記事)
【C#】配列 や List の TryGetValue
【C#】配列やListなどの中身(要素)を見る拡張メソッド Dump
【Unity】【C#】配列・リストのシャッフル
【C#】2次元配列(ジャグ配列・多次元配列)のソート
【C#】多次元配列とジャグ配列(2次元配列)のサイズ(長さ)、相互変換など


関連記事

category: C#

thread: プログラミング

janre: コンピュータ

tag: C#ライブラリ  C#リファレンス  配列操作  連想配列 
tb: 0   cm: --


プロフィール

Social

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop