文字列

【C#】文字列を安全に数値へ変換し16進数で出力する方法とコツ

C#では文字列を数値へ安全に変換するにはint.TryParseを使い、成功時に得たint値をToString("X")で16進数へ一発で変換できます。

例外を避けられ、ユーザー入力やファイル読込後の値をそのまま16進表示へ流せるので効率的です。

文字列を数値に安全に変換する基本

文字列を数値に変換する際は、入力の不確実性や例外の発生を考慮して安全に処理することが重要です。

ここでは、C#でよく使われるTryParseメソッドを中心に、例外を避けるためのポイントや文化依存の注意点、さまざまな数値型への変換方法について詳しく解説します。

TryParseメソッドの概要

C#で文字列を数値に変換する際、最も安全に扱えるのがTryParseメソッドです。

TryParseは変換の成功・失敗をブール値で返し、失敗しても例外をスローしないため、例外処理のコストを抑えつつ堅牢なコードを書けます。

TryParseとParseの違い

Parseメソッドは、文字列が有効な数値であればその数値を返しますが、無効な文字列の場合はFormatExceptionOverflowExceptionをスローします。

例外処理が必要なため、入力が不確実な場合は使いづらいことがあります。

一方、TryParseは変換に成功したかどうかをboolで返し、変換結果はoutパラメータで受け取ります。

失敗しても例外は発生しないため、例外処理の負担を減らせます。

string input = "123";
if (int.TryParse(input, out int result))
{
    Console.WriteLine($"変換成功: {result}");
}
else
{
    Console.WriteLine("変換失敗");
}
変換成功: 123

このように、TryParseは安全に変換処理を行いたい場合に推奨されます。

成功・失敗判定のパターン集

TryParseの戻り値を使った判定はシンプルですが、実際の開発では以下のようなパターンがよく使われます。

パターン説明
成功時のみ処理変換成功時にのみ処理を行うif (int.TryParse(str, out int n)) { /* 処理 */ }
失敗時にデフォルト値設定失敗時に既定値を代入int value = int.TryParse(str, out int n) ? n : 0;
失敗時にログ出力失敗をログに記録しつつ処理継続if (!int.TryParse(str, out int n)) { Console.WriteLine("変換失敗"); }

これらのパターンを使い分けることで、変換失敗時の挙動を柔軟に制御できます。

例外を避ける実装ポイント

例外はプログラムのパフォーマンスに影響を与え、また予期しないクラッシュの原因にもなります。

ここでは、FormatExceptionOverflowExceptionを避けるための具体的な対策を紹介します。

FormatException対策

FormatExceptionは、文字列が数値として不正な形式の場合に発生します。

例えば、数字以外の文字が混入している場合や空文字列の場合です。

TryParseを使うことで例外は発生しませんが、Parseを使う場合は事前に入力の形式をチェックするか、例外処理を必ず実装してください。

また、正規表現やchar.IsDigitを使って文字列の形式を検証する方法もあります。

string input = "12a3";
bool isValid = input.All(c => char.IsDigit(c));
if (isValid)
{
    int number = int.Parse(input);
}
else
{
    Console.WriteLine("不正な形式の文字列です。");
}
不正な形式の文字列です。

OverflowExceptionを防ぐ入力チェック

OverflowExceptionは、変換しようとする数値が対象の型の範囲を超えた場合に発生します。

例えば、int型の最大値を超える数値をint.Parseしようとすると例外が発生します。

TryParseは範囲外の値の場合もfalseを返すため、例外は発生しません。

Parseを使う場合は、入力値が型の範囲内かどうかを事前にチェックするか、例外処理を行う必要があります。

string input = "999999999999999999999";
if (long.TryParse(input, out long result))
{
    Console.WriteLine($"変換成功: {result}");
}
else
{
    Console.WriteLine("数値が範囲外か不正な形式です。");
}
数値が範囲外か不正な形式です。

CultureInfoを意識した数値変換

数値の文字列変換は、文化圏(カルチャ)によって小数点や桁区切りの表記が異なるため、CultureInfoを意識することが重要です。

特に小数点やカンマの扱いに注意が必要です。

小数点や桁区切りの扱い

例えば、日本の文化圏ja-JPでは小数点は「.」ですが、ドイツde-DEでは「,」が小数点として使われます。

桁区切りも逆になる場合があります。

using System.Globalization;
string input = "1,234.56";
if (double.TryParse(input, NumberStyles.Number, CultureInfo.InvariantCulture, out double value))
{
    Console.WriteLine($"変換成功: {value}");
}
else
{
    Console.WriteLine("変換失敗");
}
変換成功: 1234.56

この例では、InvariantCultureを使うことで、常に「.」を小数点として扱います。

インバリアントカルチャの利点

CultureInfo.InvariantCultureは、文化圏に依存しない固定のフォーマットを提供します。

API間のデータ受け渡しやログ出力など、文化圏に左右されない数値変換が必要な場合に便利です。

string input = "1234.56";
if (double.TryParse(input, NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
{
    Console.WriteLine($"変換成功: {value}");
}

このように、文化圏の違いによる誤変換を防ぐために、TryParseのオーバーロードでCultureInfoを指定することをおすすめします。

任意の数値型への変換

文字列から数値への変換は、intだけでなくlongushortuintなどさまざまな数値型に対応できます。

用途に応じて適切な型を選びましょう。

long・ushort・uintなどへの拡張

TryParseは多くの数値型で利用可能です。

例えば、long.TryParseは64ビット整数への変換に使えます。

string input = "9223372036854775807"; // long.MaxValue
if (long.TryParse(input, out long longValue))
{
    Console.WriteLine($"long変換成功: {longValue}");
}
else
{
    Console.WriteLine("変換失敗");
}

ushortuintも同様にTryParseが用意されています。

string input = "65535"; // ushort.MaxValue
if (ushort.TryParse(input, out ushort ushortValue))
{
    Console.WriteLine($"ushort変換成功: {ushortValue}");
}
else
{
    Console.WriteLine("変換失敗");
}

型ごとに最大値・最小値の範囲が異なるため、変換時に範囲外の値が入力されるとTryParsefalseを返します。

これにより安全に型変換が可能です。

以上のポイントを押さえることで、C#で文字列を数値に安全に変換する基本的な方法を理解できます。

TryParseを中心に例外を避け、文化圏の違いを考慮しつつ、用途に応じた数値型を選択することが大切です。

using System;
using System.Globalization;
class Program
{
    static void Main()
    {
        // 文字列をintに安全に変換する例
        string input = "12345";
        if (int.TryParse(input, out int intValue))
        {
            Console.WriteLine($"int変換成功: {intValue}");
        }
        else
        {
            Console.WriteLine("int変換失敗");
        }
        // longへの変換例
        string longInput = "9223372036854775807"; // long.MaxValue
        if (long.TryParse(longInput, out long longValue))
        {
            Console.WriteLine($"long変換成功: {longValue}");
        }
        else
        {
            Console.WriteLine("long変換失敗");
        }
        // 文化圏を指定したdouble変換例
        string doubleInput = "1234.56";
        if (double.TryParse(doubleInput, NumberStyles.Float, CultureInfo.InvariantCulture, out double doubleValue))
        {
            Console.WriteLine($"double変換成功 (InvariantCulture): {doubleValue}");
        }
        else
        {
            Console.WriteLine("double変換失敗");
        }
        // 不正な文字列の例
        string invalidInput = "12a34";
        if (int.TryParse(invalidInput, out int invalidValue))
        {
            Console.WriteLine($"変換成功: {invalidValue}");
        }
        else
        {
            Console.WriteLine("不正な文字列のため変換失敗");
        }
    }
}
int変換成功: 12345
long変換成功: 9223372036854775807
double変換成功 (InvariantCulture): 1234.56
不正な文字列のため変換失敗

このサンプルコードでは、TryParseを使ってさまざまな数値型に安全に変換しています。

文化圏を指定したdoubleの変換も行い、不正な文字列に対しては変換失敗のメッセージを表示しています。

取得した数値を16進数文字列に変換する方法

数値を16進数の文字列に変換する方法は複数あります。

ここでは代表的なToStringのフォーマット指定子を使った方法から、string.Formatや文字列補間、Convert.ToStringの基数変換、さらにはBitConverterを使ったバイト配列からの16進表示まで詳しく解説します。

ToString(“X”) フォーマット指定子

数値を16進数表記に変換する最も基本的な方法は、ToStringメソッドに"X"または"x"を指定することです。

"X"は大文字の16進数、"x"は小文字の16進数を表します。

int number = 255;
string hexUpper = number.ToString("X"); // "FF"
string hexLower = number.ToString("x"); // "ff"
Console.WriteLine($"大文字: {hexUpper}, 小文字: {hexLower}");
大文字: FF, 小文字: ff

大文字と小文字の指定

"X""x"の違いは、アルファベット部分の大文字・小文字です。

用途に応じて使い分けると見やすさや規約に合致します。

  • "X"AFが大文字
  • "x"afが小文字

パディングで固定桁数に整える

16進数の桁数を固定したい場合は、ToStringのフォーマット指定子に桁数を指定します。

例えば、4桁でゼロ埋めしたい場合は"X4"とします。

int number = 255;
string paddedHex = number.ToString("X4"); // "00FF"
Console.WriteLine(paddedHex);
00FF

このように、桁数を指定することで、出力の幅を揃えられます。

バイト列の表示や固定長のID表記などで便利です。

string.Format と補間文字列の活用

複数の数値をまとめて16進数で表示したい場合は、string.Formatや文字列補間(interpolation)を使うとコードがすっきりします。

int a = 10, b = 255, c = 4095;
string formatted = string.Format("A={0:X2}, B={1:X2}, C={2:X4}", a, b, c);
Console.WriteLine(formatted);
A=0A, B=FF, C=0FFF

文字列補間を使う場合は以下のように書けます。

string interpolated = $"A={a:X2}, B={b:X2}, C={c:X4}";
Console.WriteLine(interpolated);
A=0A, B=FF, C=0FFF

複数値をまとめて出力するテンプレート

複数の値を一括で16進数表記にしたい場合、テンプレート文字列にフォーマット指定子を埋め込むことで可読性が向上します。

特にログ出力やデバッグ時に便利です。

int[] values = { 1, 16, 255, 4096 };
string result = string.Join(", ", values.Select(v => v.ToString("X4")));
Console.WriteLine(result);
0001, 0010, 00FF, 1000

このように、配列の各要素を16進数に変換し、カンマ区切りで表示できます。

Convert.ToString メソッドによる基数変換

Convert.ToStringメソッドは、数値を任意の基数(2~36)で文字列に変換できます。

16進数変換にも使え、ToString("X")と異なり小文字固定で出力されます。

int number = 255;
string hexString = Convert.ToString(number, 16); // "ff"
Console.WriteLine(hexString);
ff

基数引数を用いた柔軟な変換

Convert.ToStringは第2引数に基数を指定できるため、2進数や8進数、36進数など多様な表現が可能です。

基数例(255の表現)
211111111
8377
10255
16ff
3673
int number = 255;
Console.WriteLine(Convert.ToString(number, 2));  // 2進数
Console.WriteLine(Convert.ToString(number, 8));  // 8進数
Console.WriteLine(Convert.ToString(number, 16)); // 16進数(小文字)
11111111
377
ff

ただし、Convert.ToStringは16進数を小文字で返すため、大文字にしたい場合はToUpper()を使うか、ToString("X")を使うのが一般的です。

BitConverter を用いたバイト配列からの変換

数値をバイト配列に変換し、そのバイト列を16進数で表示したい場合はBitConverterクラスが便利です。

特にバイナリデータの可視化や通信データのデバッグに役立ちます。

int number = 305419896; // 0x12345678
byte[] bytes = BitConverter.GetBytes(number);
string hex = BitConverter.ToString(bytes);
Console.WriteLine(hex);
78-56-34-12

エンディアンを考慮した16進表示

BitConverterはシステムのエンディアン(バイト順)に従ってバイト配列を返します。

上記の例ではリトルエンディアン環境であるため、下位バイトから順に並んでいます。

ビッグエンディアンで表示したい場合は、配列を逆順にする必要があります。

byte[] bigEndianBytes = bytes.Reverse().ToArray();
string bigEndianHex = BitConverter.ToString(bigEndianBytes);
Console.WriteLine(bigEndianHex);
12-34-56-78

このように、エンディアンを意識してバイト配列を操作することで、正しい16進数表記を得られます。

これらの方法を使い分けることで、用途に応じた16進数文字列の生成が可能です。

ToString("X")はシンプルで使いやすく、Convert.ToStringは基数変換の汎用性が高く、BitConverterはバイト列の可視化に最適です。

用途に合わせて適切な方法を選択してください。

using System;
using System.Linq;
class Program
{
    static void Main()
    {
        int number = 255;
        // ToString("X")で大文字16進数
        string hexUpper = number.ToString("X");
        Console.WriteLine($"ToString(\"X\"): {hexUpper}"); // FF
        // ToString("x")で小文字16進数
        string hexLower = number.ToString("x");
        Console.WriteLine($"ToString(\"x\"): {hexLower}"); // ff
        // パディングで4桁固定
        string paddedHex = number.ToString("X4");
        Console.WriteLine($"パディング4桁: {paddedHex}"); // 00FF
        // string.Formatで複数値を16進数表示
        int a = 10, b = 255, c = 4095;
        string formatted = string.Format("A={0:X2}, B={1:X2}, C={2:X4}", a, b, c);
        Console.WriteLine(formatted); // A=0A, B=FF, C=0FFF
        // 文字列補間で同様の出力
        string interpolated = $"A={a:X2}, B={b:X2}, C={c:X4}";
        Console.WriteLine(interpolated); // A=0A, B=FF, C=0FFF
        // 配列の16進数表示
        int[] values = { 1, 16, 255, 4096 };
        string joinedHex = string.Join(", ", values.Select(v => v.ToString("X4")));
        Console.WriteLine($"配列の16進数: {joinedHex}"); // 0001, 0010, 00FF, 1000
        // Convert.ToStringで16進数(小文字)
        string convertHex = Convert.ToString(number, 16);
        Console.WriteLine($"Convert.ToString: {convertHex}"); // ff
        // Convert.ToStringで2進数、8進数も表示
        Console.WriteLine($"2進数: {Convert.ToString(number, 2)}");  // 11111111
        Console.WriteLine($"8進数: {Convert.ToString(number, 8)}");  // 377
        // BitConverterでバイト配列から16進数表示
        int val = 0x12345678;
        byte[] bytes = BitConverter.GetBytes(val);
        string byteHex = BitConverter.ToString(bytes);
        Console.WriteLine($"BitConverter (リトルエンディアン): {byteHex}"); // 78-56-34-12
        // ビッグエンディアンに変換して表示
        byte[] bigEndianBytes = bytes.Reverse().ToArray();
        string bigEndianHex = BitConverter.ToString(bigEndianBytes);
        Console.WriteLine($"BitConverter (ビッグエンディアン): {bigEndianHex}"); // 12-34-56-78
    }
}
ToString("X"): FF
ToString("x"): ff
パディング4桁: 00FF
A=0A, B=FF, C=0FFF
A=0A, B=FF, C=0FFF
配列の16進数: 0001, 0010, 00FF, 1000
Convert.ToString: ff
2進数: 11111111
8進数: 377
BitConverter (リトルエンディアン): 78-56-34-12
BitConverter (ビッグエンディアン): 12-34-56-78

このサンプルコードでは、16進数表記の基本から応用までを網羅しています。

ToStringのフォーマット指定子で大文字・小文字や桁数を調整し、string.Formatや文字列補間で複数値をまとめて表示。

Convert.ToStringで基数変換を行い、BitConverterでバイト配列の16進数表記も確認できます。

実用例で学ぶ文字列→数値→16進数の流れ

文字列から数値に変換し、その数値を16進数表記に変換する一連の流れは、実際のアプリケーションでよく使われます。

ここでは、ユーザー入力のリアルタイム変換、ファイル読み込みデータの可視化、APIレスポンスの整形表示といった具体的なシナリオを通じて、実践的なコード例を示します。

ユーザー入力をリアルタイムで16進表示

ユーザーが入力した数値文字列をリアルタイムで16進数に変換して表示するUIは、デバッグツールや設定画面などで役立ちます。

ここではWindowsフォーム(WinForms)とWPFでの実装例を示します。

WinForms の TextBox 例

WinFormsでは、TextBoxTextChangedイベントを利用して、入力が変わるたびに変換処理を行い、別のLabelTextBoxに16進数を表示します。

using System;
using System.Windows.Forms;
public class HexConverterForm : Form
{
    private TextBox inputTextBox;
    private Label hexLabel;
    public HexConverterForm()
    {
        inputTextBox = new TextBox { Location = new System.Drawing.Point(20, 20), Width = 200 };
        hexLabel = new Label { Location = new System.Drawing.Point(20, 60), Width = 200 };
        inputTextBox.TextChanged += InputTextBox_TextChanged;
        Controls.Add(inputTextBox);
        Controls.Add(hexLabel);
        Text = "リアルタイム16進数変換";
        Width = 260;
        Height = 140;
    }
    private void InputTextBox_TextChanged(object sender, EventArgs e)
    {
        string input = inputTextBox.Text.Trim();
        if (int.TryParse(input, out int number))
        {
            hexLabel.Text = $"16進数: {number.ToString("X")}";
        }
        else
        {
            hexLabel.Text = "無効な数値です";
        }
    }
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.Run(new HexConverterForm());
    }
}

このコードでは、ユーザーがTextBoxに数値を入力すると、TextChangedイベントでTryParseを使って安全に整数に変換し、成功すれば16進数表記をLabelに表示します。

無効な入力の場合はエラーメッセージを表示します。

WPF バインディングでの双方向変換

WPFでは、データバインディングとIValueConverterを使って、入力と表示を双方向に連動させることができます。

以下は、TextBoxの入力を整数に変換し、16進数表記を別のTextBlockに表示する例です。

// HexConverter.cs
using System;
using System.Globalization;
using System.Windows.Data;
public class HexConverter : IValueConverter
{
    // 数値(int)→16進数文字列
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is int number)
        {
            return number.ToString("X");
        }
        return "";
    }
    // 16進数文字列→数値(int)
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string str = value as string;
        if (int.TryParse(str, out int number))
        {
            return number;
        }
        return 0;
    }
}
<!-- MainWindow.xaml -->
<Window x:Class="HexConverterApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:local="clr-namespace:HexConverterApp"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF 16進数変換" Height="150" Width="300">
    <Window.Resources>
        <local:HexConverter x:Key="HexConverter"/>
    </Window.Resources>
    <StackPanel Margin="10">
        <TextBox x:Name="InputTextBox" Width="200" Height="25" Margin="0,0,0,10"
                 Text="{Binding Path=InputValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Text="{Binding Path=InputValue, Converter={StaticResource HexConverter}}" FontSize="16"/>
    </StackPanel>
</Window>
// MainWindow.xaml.cs
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace HexConverterApp
{
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        private int inputValue;
        public int InputValue
        {
            get => inputValue;
            set
            {
                if (inputValue != value)
                {
                    inputValue = value;
                    OnPropertyChanged();
                }
            }
        }
        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;
        }
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
    }
}

この例では、TextBoxの入力は整数型のInputValueプロパティにバインドされ、HexConverterが整数から16進数文字列への変換を担当します。

双方向バインディングにより、UIの更新がリアルタイムで反映されます。

ファイル読み込みデータの可視化

CSVファイルなどのテキストデータに含まれる数値を読み込み、16進数表記でログや画面に出力するケースも多いです。

以下はCSVの数値列を16進数に変換してコンソールに出力する例です。

using System;
using System.IO;
class CsvHexLogger
{
    static void Main()
    {
        string filePath = "data.csv";
        if (!File.Exists(filePath))
        {
            Console.WriteLine("ファイルが存在しません。");
            return;
        }
        using (var reader = new StreamReader(filePath))
        {
            string line;
            int lineNumber = 0;
            while ((line = reader.ReadLine()) != null)
            {
                lineNumber++;
                var columns = line.Split(',');
                // 1列目の数値を16進数に変換して表示(例)
                if (columns.Length > 0 && int.TryParse(columns[0], out int number))
                {
                    Console.WriteLine($"行{lineNumber}: {columns[0]} → 0x{number.ToString("X")}");
                }
                else
                {
                    Console.WriteLine($"行{lineNumber}: 数値変換失敗");
                }
            }
        }
    }
}

例えば、data.csvの内容が以下の場合、

10,foo,bar
255,baz,qux
abc,def,ghi

実行結果は、

行1: 10 → 0xA
行2: 255 → 0xFF
行3: 数値変換失敗

となり、数値列を16進数で可視化できます。

APIレスポンスの整形表示

JSON形式のAPIレスポンスに含まれる数値フィールドを16進数表記に置換して表示することもあります。

以下は、JSON文字列をパースし、特定の数値フィールドを16進数文字列に変換して再構築する例です。

using System;
using System.Text.Json;
using System.Text.Json.Nodes;
class JsonHexFormatter
{
    static void Main()
    {
        string json = @"{
            ""id"": 123,
            ""name"": ""Sample"",
            ""value"": 255,
            ""details"": {
                ""code"": 4096,
                ""description"": ""Test""
            }
        }";
        var jsonNode = JsonNode.Parse(json);
        // 数値フィールドを16進数に変換する関数
        void ConvertNumberToHex(JsonNode node)
        {
            if (node is JsonObject obj)
            {
                foreach (var key in obj.Keys)
                {
                    var child = obj[key];
                    if (child is JsonValue val && val.TryGetValue<int>(out int intValue))
                    {
                        obj[key] = $"0x{intValue.ToString("X")}";
                    }
                    else if (child is JsonObject || child is JsonArray)
                    {
                        ConvertNumberToHex(child);
                    }
                }
            }
            else if (node is JsonArray arr)
            {
                for (int i = 0; i < arr.Count; i++)
                {
                    var child = arr[i];
                    if (child is JsonValue val && val.TryGetValue<int>(out int intValue))
                    {
                        arr[i] = $"0x{intValue.ToString("X")}";
                    }
                    else if (child is JsonObject || child is JsonArray)
                    {
                        ConvertNumberToHex(child);
                    }
                }
            }
        }
        ConvertNumberToHex(jsonNode);
        string formattedJson = jsonNode.ToJsonString(new JsonSerializerOptions { WriteIndented = true });
        Console.WriteLine(formattedJson);
    }
}

実行結果は以下のように、数値フィールドが16進数文字列に置き換わります。

{
  "id": "0x7B",
  "name": "Sample",
  "value": "0xFF",
  "details": {
    "code": "0x1000",
    "description": "Test"
  }
}

この方法は、APIレスポンスの数値を16進数で表示したい場合や、ログの可読性を高めたい場合に有効です。

System.Text.JsonJsonNodeを使うことで、柔軟にJSON構造を操作できます。

変換時に遭遇しがちなエラーと対処

文字列から数値への変換や、その数値を16進数に変換する際には、さまざまなエラーや注意点が発生しやすいです。

ここでは、特に多く遭遇する「Nullまたは空文字の扱い」「不正文字混入の検知」「負の数の16進表現の落とし穴」について具体的に解説し、適切な対処方法を示します。

Nullまたは空文字の扱い

文字列がnullや空文字("")の場合、int.TryParseなどの変換メソッドは変換に失敗し、falseを返します。

例外は発生しませんが、これを適切に処理しないと意図しない動作やエラーにつながることがあります。

string nullString = null;
string emptyString = "";
bool resultNull = int.TryParse(nullString, out int numberNull);
bool resultEmpty = int.TryParse(emptyString, out int numberEmpty);
Console.WriteLine($"null文字列の変換結果: {resultNull}, 値: {numberNull}");
Console.WriteLine($"空文字列の変換結果: {resultEmpty}, 値: {numberEmpty}");
null文字列の変換結果: False, 値: 0
空文字列の変換結果: False, 値: 0

TryParseは失敗時にoutパラメータに0をセットするため、変換失敗と値の区別がつきにくい場合があります。

したがって、変換結果のbool値を必ずチェックし、失敗時の処理を明確にすることが重要です。

また、nullや空文字を事前にチェックして、早期に処理を分けることも有効です。

if (string.IsNullOrEmpty(input))
{
    Console.WriteLine("入力が空です。");
}
else if (int.TryParse(input, out int number))
{
    Console.WriteLine($"変換成功: {number}");
}
else
{
    Console.WriteLine("変換失敗");
}

不正文字混入の検知

数値変換に失敗する主な原因は、文字列に数字以外の不正な文字が混入していることです。

例えば、アルファベットや記号、空白などが含まれていると変換は失敗します。

string invalidInput = "12a34";
bool success = int.TryParse(invalidInput, out int number);
Console.WriteLine($"変換成功: {success}, 値: {number}");
変換成功: False, 値: 0

不正文字を検知してユーザーにフィードバックを行う場合は、TryParseの結果を利用するほか、正規表現や文字列の検査で事前にチェックする方法もあります。

using System.Text.RegularExpressions;
string input = "1234a";
bool isNumeric = Regex.IsMatch(input, @"^\d+$");
if (isNumeric)
{
    int number = int.Parse(input);
    Console.WriteLine($"変換成功: {number}");
}
else
{
    Console.WriteLine("不正な文字が含まれています。");
}

この例では、数字のみで構成されているかを正規表現で判定し、不正文字の混入を検知しています。

負の数の16進表現の落とし穴

負の整数を16進数に変換する際、符号付き整数の内部表現である「2の補数」が影響し、直感的でない結果になることがあります。

int negativeNumber = -1;
string hex = negativeNumber.ToString("X");
Console.WriteLine($"-1 の16進数表記: {hex}");
-1 の16進数表記: FFFFFFFF

2の補数表現との関係

C#の整数型は符号付きで、負の数は2の補数表現で内部的に保持されています。

intは32ビットなので、-1は全ビットが1の状態0xFFFFFFFFとして扱われます。

そのため、ToString("X")で16進数に変換すると、FFFFFFFFと表示されます。

この挙動は仕様であり、負の数を16進数で表す場合は注意が必要です。

もし負の数を符号なしの16進数として扱いたい場合は、型をuintにキャストしてから変換すると分かりやすくなります。

int negativeNumber = -1;
uint unsignedNumber = unchecked((uint)negativeNumber);
string hex = unsignedNumber.ToString("X");
Console.WriteLine($"符号なしとしての16進数表記: {hex}");
符号なしとしての16進数表記: FFFFFFFF

また、負の数を単純に符号付きのまま16進数表記したい場合は、符号を明示的に扱うか、符号なし型に変換してから表示する方法を検討してください。

これらのエラーや注意点を理解し、適切に対処することで、文字列から数値への変換や16進数表記の処理を安全かつ正確に行えます。

特に入力の検証と変換結果のチェックは必須です。

拡張メソッドでコードをスッキリ

C#の拡張メソッドを活用すると、文字列から数値への変換や数値の16進数表記を簡潔に記述でき、コードの可読性や再利用性が向上します。

ここでは、文字列の安全な数値変換を行うTryParseSafeメソッドと、整数を16進数文字列に変換するToHexStringメソッドを例に、拡張メソッドの実装と使い方を示します。

StringExtensions.TryParseSafe

TryParseSafeは、文字列に対して安全に数値変換を試みる拡張メソッドです。

nullや空文字、変換失敗時に例外を発生させず、デフォルト値を返すことでコードの冗長なチェックを減らせます。

public static class StringExtensions
{
    /// <summary>
    /// 文字列をintに安全に変換します。変換失敗時はデフォルト値を返します。
    /// </summary>
    /// <param name="str">変換対象の文字列</param>
    /// <param name="defaultValue">変換失敗時に返すデフォルト値(省略可、既定は0)</param>
    /// <returns>変換成功時は数値、失敗時はdefaultValue</returns>
    public static int TryParseSafe(this string str, int defaultValue = 0)
    {
        if (string.IsNullOrWhiteSpace(str))
        {
            return defaultValue;
        }
        return int.TryParse(str, out int result) ? result : defaultValue;
    }
}

このメソッドを使うと、以下のようにシンプルに数値変換が書けます。

string input = "123";
int number = input.TryParseSafe(); // 123
string invalidInput = "abc";
int defaultNumber = invalidInput.TryParseSafe(10); // 10

IntExtensions.ToHexString

ToHexStringは、整数型に対して16進数文字列を返す拡張メソッドです。

大文字・小文字の指定や桁数のパディングもオプションで指定可能です。

public static class IntExtensions
{
    /// <summary>
    /// 整数を16進数文字列に変換します。
    /// </summary>
    /// <param name="value">変換対象の整数</param>
    /// <param name="uppercase">大文字で出力するか(既定はtrue)</param>
    /// <param name="padLength">桁数を指定し、足りない場合は0でパディング(省略可)</param>
    /// <returns>16進数文字列</returns>
    public static string ToHexString(this int value, bool uppercase = true, int padLength = 0)
    {
        string format = uppercase ? "X" : "x";
        if (padLength > 0)
        {
            format += padLength.ToString();
        }
        return value.ToString(format);
    }
}

この拡張メソッドを使うと、以下のように簡潔に16進数表記が得られます。

int number = 255;
string hex1 = number.ToHexString();          // "FF"
string hex2 = number.ToHexString(false);     // "ff"
string hex3 = number.ToHexString(true, 4);   // "00FF"

サンプル実装と使用例

これらの拡張メソッドをまとめて使う例を示します。

文字列から安全に数値を取得し、その数値を16進数で表示する流れがスッキリ書けます。

using System;
public static class StringExtensions
{
    public static int TryParseSafe(this string str, int defaultValue = 0)
    {
        if (string.IsNullOrWhiteSpace(str))
        {
            return defaultValue;
        }
        return int.TryParse(str, out int result) ? result : defaultValue;
    }
}
public static class IntExtensions
{
    public static string ToHexString(this int value, bool uppercase = true, int padLength = 0)
    {
        string format = uppercase ? "X" : "x";
        if (padLength > 0)
        {
            format += padLength.ToString();
        }
        return value.ToString(format);
    }
}
class Program
{
    static void Main()
    {
        string[] inputs = { "123", "abc", null, "255", "  42  ", "" };
        foreach (var input in inputs)
        {
            int number = input.TryParseSafe(-1);
            string hex = number >= 0 ? number.ToHexString(true, 4) : "変換失敗";
            Console.WriteLine($"入力: \"{input ?? "null"}\" → 数値: {number} → 16進数: {hex}");
        }
    }
}
入力: "123" → 数値: 123 → 16進数: 007B
入力: "abc" → 数値: -1 → 16進数: 変換失敗
入力: "null" → 数値: -1 → 16進数: 変換失敗
入力: "255" → 数値: 255 → 16進数: 00FF
入力: "  42  " → 数値: 42 → 16進数: 002A
入力: "" → 数値: -1 → 16進数: 変換失敗

このサンプルでは、入力文字列がnullや空文字、変換不能な文字列の場合はデフォルト値-1を返し、16進数表記は「変換失敗」と表示しています。

正常な数値は4桁の大文字16進数で表示され、コードが非常に読みやすくなっています。

拡張メソッドを活用することで、変換処理の共通化とコードの簡潔化が実現でき、保守性も向上します。

パフォーマンスを意識した大量変換

大量の文字列変換や数値の16進数表記を行う場合、単純な方法ではパフォーマンスが低下することがあります。

ここでは、Span<char>TryFormatの活用、StringBuilderによるバッファリング、さらにLINQforeachの速度比較を通じて、効率的な変換処理のポイントを解説します。

Span<char> とTryFormatの利用

Span<char>は、メモリの連続領域を表す軽量な構造体で、文字列操作の際に新たな文字列を生成せずに済むため、GC(ガベージコレクション)負荷を減らせます。

TryFormatは数値型に実装されているメソッドで、Span<char>に直接フォーマット済みの文字列を書き込めるため高速です。

using System;
class Program
{
    static void Main()
    {
        int number = 123456;
        Span<char> buffer = stackalloc char[8]; // 十六進数で最大8桁(intの場合)
        // TryFormatで16進数に変換(大文字、パディングなし)
        bool success = number.TryFormat(buffer, out int charsWritten, format: "X");
        if (success)
        {
            string hexString = new string(buffer.Slice(0, charsWritten));
            Console.WriteLine($"16進数: {hexString}");
        }
    }
}
16進数: 1E240

TryFormatは例外を発生させず、バッファに直接書き込むため、文字列の生成コストを抑えられます。

大量の変換処理で特に効果的です。

StringBuilderでのバッファリング

大量の文字列を連結して出力する場合、StringBuilderを使うことで文字列の再生成を減らし、パフォーマンスを向上させられます。

特にループ内で多数の16進数文字列を連結する際に有効です。

using System;
using System.Text;
class Program
{
    static void Main()
    {
        int[] numbers = { 10, 255, 4096, 65535 };
        var sb = new StringBuilder();
        foreach (var num in numbers)
        {
            sb.Append(num.ToString("X4"));
            sb.Append(", ");
        }
        // 最後のカンマと空白を削除
        if (sb.Length >= 2)
        {
            sb.Length -= 2;
        }
        Console.WriteLine(sb.ToString());
    }
}
000A, 00FF, 1000, FFFF

StringBuilderは内部でバッファを拡張しながら文字列を構築するため、+演算子での連結より効率的です。

大量の文字列連結が必要な場合は積極的に使いましょう。

LINQとforeachの速度比較

LINQはコードを簡潔に書ける反面、パフォーマンス面ではforeachループに劣ることがあります。

大量データの変換処理では、速度が重要な場合にどちらを使うか検討が必要です。

以下は、100万件の整数配列を16進数文字列に変換し、LINQforeachで処理時間を比較する例です。

using System;
using System.Diagnostics;
using System.Linq;
class Program
{
    static void Main()
    {
        int[] numbers = Enumerable.Range(0, 1_000_000).ToArray();
        // LINQを使った変換
        var sw = Stopwatch.StartNew();
        var linqResult = numbers.Select(n => n.ToString("X")).ToArray();
        sw.Stop();
        Console.WriteLine($"LINQ処理時間: {sw.ElapsedMilliseconds} ms");
        // foreachを使った変換
        sw.Restart();
        string[] foreachResult = new string[numbers.Length];
        for (int i = 0; i < numbers.Length; i++)
        {
            foreachResult[i] = numbers[i].ToString("X");
        }
        sw.Stop();
        Console.WriteLine($"foreach処理時間: {sw.ElapsedMilliseconds} ms");
    }
}

実行結果の例(環境により異なります):

LINQ処理時間: 61 ms
foreach処理時間: 114 ms

この結果から、LINQの方が高速であることがわかります。

これらのテクニックを組み合わせることで、大量の文字列→数値→16進数変換処理を効率的に実装できます。

Span<char>TryFormatでメモリ割り当てを抑え、StringBuilderで文字列連結を最適化し、ループ処理はforeachで高速化を図ることがポイントです。

まとめ

この記事では、C#で文字列を安全に数値に変換し、その数値を16進数表記に変換する方法を詳しく解説しました。

TryParseを使った例外を避ける変換や文化圏を考慮した処理、ToString("X")Convert.ToStringによる16進数変換、さらに拡張メソッドでコードを簡潔にするテクニックも紹介しています。

大量データ処理ではSpan<char>StringBuilderを活用し、パフォーマンスを意識した実装が可能です。

これらを活用することで、安全かつ効率的に文字列→数値→16進数変換を行えます。

関連記事

Back to top button
目次へ