この記事では、数値データや日付と時刻のデータなどの書式設定されたデータを、表示と保存のためにどのように処理するかを調べます。
.NET を使用して開発する場合は、カルチャに依存する書式を使用して、数値や日付などの文字列以外のデータをユーザー インターフェイスに表示します。 文字列形式で文字列以外のデータを保持するには、インバリアント カルチャ で書式設定を使用します。 カルチャに依存する書式を使用して、文字列形式で数値または日付と時刻のデータを保持しないでください。
書式設定されたデータを表示する
数値や日付や時刻などの文字列以外のデータをユーザーに表示する場合は、ユーザーのカルチャ設定を使用して書式設定します。 既定では、次のすべてが書式設定操作で現在のカルチャを使用します。
- C# および Visual Basic コンパイラ でサポートされる挿入文字列。
- C# を使用する文字列連結操作、または Visual Basic 連結演算子、または String.Concat メソッドを直接呼び出す文字列連結操作。
- String.Format メソッド。
- 数値型と日付と時刻の型の
ToStringメソッド。
指定されたカルチャの規則または インバリアント カルチャを使用して文字列を書式設定することを明示的に指定するには、次の操作を行います。
String.Format メソッドと
ToStringメソッドを使用する場合は、providerや String.Format(IFormatProvider, String, Object[])などの DateTime.ToString(IFormatProvider) パラメーターを持つオーバーロードを呼び出し、CultureInfo.CurrentCulture プロパティ、目的のカルチャを表す CultureInfo インスタンス、または CultureInfo.InvariantCulture プロパティを渡します。文字列連結の場合、コンパイラが暗黙的な変換を実行できないようにします。 代わりに、
ToStringパラメーターを持つproviderオーバーロードを呼び出して、明示的な変換を実行します。 たとえば、コンパイラは、次のコードで Double 値を文字列に変換するときに、現在のカルチャを暗黙的に使用します。string concat1 = "The amount is " + 126.03 + "."; Console.WriteLine(concat1);Dim concat1 As String = "The amount is " & 126.03 & "." Console.WriteLine(concat1)代わりに、次のコードのように、Double.ToString(IFormatProvider) メソッドを呼び出すことによって、変換で書式設定規則が使用されるカルチャを明示的に指定できます。
string concat2 = "The amount is " + 126.03.ToString(CultureInfo.InvariantCulture) + "."; Console.WriteLine(concat2);Dim concat2 As String = "The amount is " & 126.03.ToString(CultureInfo.InvariantCulture) & "." Console.WriteLine(concat2)文字列補間の場合は、挿入文字列を String インスタンスに割り当てるのではなく、FormattableStringに割り当てます。 その後、その FormattableString.ToString() メソッドを呼び出して、現在のカルチャの規則を反映する結果文字列を生成するか、FormattableString.ToString(IFormatProvider) メソッドを呼び出して、指定したカルチャの規則を反映した結果文字列を生成できます。
また、書式設定可能な文字列を静的 FormattableString.Invariant メソッドに渡して、インバリアント カルチャの規則を反映する結果文字列を生成することもできます。 この方法の例を次に示します。 (この例からの出力は、
en-USの現在のカルチャを反映しています)。using System; using System.Globalization; class Program { static void Main() { Decimal value = 126.03m; FormattableString amount = $"The amount is {value:C}"; Console.WriteLine(amount.ToString()); Console.WriteLine(amount.ToString(new CultureInfo("fr-FR"))); Console.WriteLine(FormattableString.Invariant(amount)); } } // The example displays the following output: // The amount is $126.03 // The amount is 126,03 € // The amount is ¤126.03Imports System.Globalization Module Program Sub Main() Dim value As Decimal = 126.03 Dim amount As FormattableString = $"The amount is {value:C}" Console.WriteLine(amount.ToString()) Console.WriteLine(amount.ToString(new CultureInfo("fr-FR"))) Console.WriteLine(FormattableString.Invariant(amount)) End Sub End Module ' The example displays the following output: ' The amount is $126.03 ' The amount is 126,03 € ' The amount is ¤126.03注
C# を使用し、インバリアント カルチャを使用して書式設定する場合は、String.Create(IFormatProvider, DefaultInterpolatedStringHandler) を呼び出し、最初のパラメーターに CultureInfo.InvariantCulture を渡す方がパフォーマンスが高くなります。 詳細については、「C# 10 および .NET 6での文字列補間」を参照してください。
書式設定されたデータを保持する
文字列以外のデータは、バイナリ データまたは書式設定されたデータとして保持できます。 書式設定されたデータとして保存する場合は、provider パラメーターを含む書式設定メソッドのオーバーロードを呼び出し、CultureInfo.InvariantCulture プロパティを渡す必要があります。 インバリアント カルチャは、カルチャとコンピューターに依存しない書式設定されたデータに対して一貫した形式を提供します。 これに対し、インバリアント カルチャ以外のカルチャを使用して書式設定されたデータを保持するには、いくつかの制限があります。
- データは、異なるカルチャを持つシステムでデータを取得する場合や、現在のシステムのユーザーが現在のカルチャを変更してデータを取得しようとした場合に使用できなくなる可能性があります。
- 特定のコンピューター上のカルチャのプロパティは、標準値とは異なる場合があります。 ユーザーはいつでもカルチャに依存する表示設定をカスタマイズできます。 このため、ユーザーがカルチャ設定をカスタマイズした後に、システムに保存される書式設定されたデータを読み取れない場合があります。 コンピューター間での書式設定されたデータの移植性はさらに制限される可能性があります。
- 数値や日付と時刻の書式を管理する国際標準、地域標準、または国標準は時間の経過と同時に変化し、これらの変更は Windows オペレーティング システムの更新プログラムに組み込まれます。 書式設定規則が変更されると、前の規則を使用して書式設定されたデータが読み取れなくなる可能性があります。
次の例は、カルチャに依存する書式設定を使用してデータを保持することによって生じる移植性の制限を示しています。 この例では、日付と時刻の値の配列をファイルに保存します。 これらは、英語 (米国) カルチャの規則を使用して書式設定されます。 アプリケーションは、現在のカルチャをフランス語 (スイス) に変更した後、現在のカルチャの書式設定規則を使用して保存された値の読み取りを試みます。 2つのデータ項目を読み取る試みがFormatException例外をスローし、日付の配列にはMinValueに等しい2つの誤った要素が含まれることになりました。
using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;
public class Example
{
private static string filename = @".\dates.dat";
public static void Main()
{
DateTime[] dates = { new DateTime(1758, 5, 6, 21, 26, 0),
new DateTime(1818, 5, 5, 7, 19, 0),
new DateTime(1870, 4, 22, 23, 54, 0),
new DateTime(1890, 9, 8, 6, 47, 0),
new DateTime(1905, 2, 18, 15, 12, 0) };
// Write the data to a file using the current culture.
WriteData(dates);
// Change the current culture.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-CH");
// Read the data using the current culture.
DateTime[] newDates = ReadData();
foreach (var newDate in newDates)
Console.WriteLine(newDate.ToString("g"));
}
private static void WriteData(DateTime[] dates)
{
StreamWriter sw = new StreamWriter(filename, false, Encoding.UTF8);
for (int ctr = 0; ctr < dates.Length; ctr++) {
sw.Write("{0}", dates[ctr].ToString("g", CultureInfo.CurrentCulture));
if (ctr < dates.Length - 1) sw.Write("|");
}
sw.Close();
}
private static DateTime[] ReadData()
{
bool exceptionOccurred = false;
// Read file contents as a single string, then split it.
StreamReader sr = new StreamReader(filename, Encoding.UTF8);
string output = sr.ReadToEnd();
sr.Close();
string[] values = output.Split( new char[] { '|' } );
DateTime[] newDates = new DateTime[values.Length];
for (int ctr = 0; ctr < values.Length; ctr++) {
try {
newDates[ctr] = DateTime.Parse(values[ctr], CultureInfo.CurrentCulture);
}
catch (FormatException) {
Console.WriteLine($"Failed to parse {values[ctr]}");
exceptionOccurred = true;
}
}
if (exceptionOccurred) Console.WriteLine();
return newDates;
}
}
// The example displays the following output:
// Failed to parse 4/22/1870 11:54 PM
// Failed to parse 2/18/1905 3:12 PM
//
// 05.06.1758 21:26
// 05.05.1818 07:19
// 01.01.0001 00:00
// 09.08.1890 06:47
// 01.01.0001 00:00
// 01.01.0001 00:00
Imports System.Globalization
Imports System.IO
Imports System.Text
Imports System.Threading
Module Example
Private filename As String = ".\dates.dat"
Public Sub Main()
Dim dates() As Date = {#5/6/1758 9:26PM#, #5/5/1818 7:19AM#, _
#4/22/1870 11:54PM#, #9/8/1890 6:47AM#, _
#2/18/1905 3:12PM#}
' Write the data to a file using the current culture.
WriteData(dates)
' Change the current culture.
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture("fr-CH")
' Read the data using the current culture.
Dim newDates() As Date = ReadData()
For Each newDate In newDates
Console.WriteLine(newDate.ToString("g"))
Next
End Sub
Private Sub WriteData(dates() As Date)
Dim sw As New StreamWriter(filename, False, Encoding.Utf8)
For ctr As Integer = 0 To dates.Length - 1
sw.Write("{0}", dates(ctr).ToString("g", CultureInfo.CurrentCulture))
If ctr < dates.Length - 1 Then sw.Write("|")
Next
sw.Close()
End Sub
Private Function ReadData() As Date()
Dim exceptionOccurred As Boolean = False
' Read file contents as a single string, then split it.
Dim sr As New StreamReader(filename, Encoding.Utf8)
Dim output As String = sr.ReadToEnd()
sr.Close()
Dim values() As String = output.Split({"|"c})
Dim newDates(values.Length - 1) As Date
For ctr As Integer = 0 To values.Length - 1
Try
newDates(ctr) = DateTime.Parse(values(ctr), CultureInfo.CurrentCulture)
Catch e As FormatException
Console.WriteLine("Failed to parse {0}", values(ctr))
exceptionOccurred = True
End Try
Next
If exceptionOccurred Then Console.WriteLine()
Return newDates
End Function
End Module
' The example displays the following output:
' Failed to parse 4/22/1870 11:54 PM
' Failed to parse 2/18/1905 3:12 PM
'
' 05.06.1758 21:26
' 05.05.1818 07:19
' 01.01.0001 00:00
' 09.08.1890 06:47
' 01.01.0001 00:00
' 01.01.0001 00:00
'
ただし、CultureInfo.CurrentCulture と CultureInfo.InvariantCultureの呼び出しで DateTime.ToString(String, IFormatProvider) プロパティを DateTime.Parse(String, IFormatProvider) に置き換えた場合、次の出力に示すように、永続化された日付と時刻のデータが正常に復元されます。
06.05.1758 21:26
05.05.1818 07:19
22.04.1870 23:54
08.09.1890 06:47
18.02.1905 15:12
.NET