PowerShellでCSVを読み込み・編集・出力する方法|Import-Csv・Export-Csvの基本と文字化け対策

当ページのリンクには広告が含まれています。
PowerShellでCSVファイルを読み込み条件でデータを抽出・編集して別のCSVファイルへ出力する流れを示したアイキャッチ画像

CSVの中身をちょっと確認したいだけなのに、Excelで開いたら列がズレていたり、文字化けしていたり、結構面倒ですよね。筆者はそのたびにExcelを開き直すのが地味に面倒で、最近はPowerShellでサクッと確認する方法に落ち着きました。

PowerShellのImport-CsvExport-CsvでCSVの1行が列名付きのオブジェクトとして扱えるので、条件で絞り込んだり列を追加したりがかなり直感的にできます。この記事ではその基本の使い方と、筆者も一度ハマった文字化けまわりの対処法をまとめました。実際に手元のPowerShell 7.6で動かした結果も載せているので、そのままコピペして試してもらえると思います。

PowerShellでCSVを扱う基本はImport-Csvで読み込み、Where-ObjectSelect-Objectで加工し、Export-Csvで保存する流れです。日本語を含むCSVではWindows PowerShell 5.1とPowerShell 7系で既定の文字コードが異なるため、利用環境に合わせて-Encodingを指定します。

次の環境で動作を確認しています

Windows 11 Home 25H2
PowerShell 7.6.1

目次

この記事でわかること

  • Import-CsvでCSVファイルを読み込む方法
  • 読み込んだ内容を確認したり条件で絞り込んだりする方法
  • 列を追加・変更してExport-Csvで出力する方法
  • Excelで日本語が化ける・消えるときの原因と対処法(PowerShell 5.1と7以降の違い)

今回使うサンプルCSV

今回はこんな感じのCSVを使って説明します。ファイル名はusers.csvとしました。

users.csv
Name,Department,Age
ちゅん一郎,営業部,41
ちゅん次郎,開発部,37
ちゅん三郎,営業部,34
ちゅん狂四郎,総務部,31
ちゅんゴロー,情報システム部

わざとではあるのですが、最後の「ちゅんゴロー」だけAgeの値が抜けています。実務のCSVでもこういう欠損値混じりのデータは普通にあるので、この記事ではあえてこのまま進めます。この行がところどころで地味に効いてくるので、注目してみてください。

CSVを保存したフォルダーからPowerShellを開くと長いパスを毎回入力せずにコマンドを試せます。

Import-CsvでCSVを読み込む

Import-CsvコマンドレットはCSVの各行をヘッダー名付きのPowerShellオブジェクトとして読み込むコマンドレットです。列名がプロパティ名になるため、名前を指定して値を取り出したり、条件で行を絞り込んだりできます。

PowerShell
$data = Import-Csv -Path "C:\work\users.csv"
$data
実行結果
Name         Department     Age
----         ----------     ---
ちゅん一郎   営業部         41
ちゅん次郎   開発部         37
ちゅん三郎   営業部         34
ちゅん狂四郎 総務部         31
ちゅんゴロー 情報システム部

CSVの1行が1つのオブジェクトとして読み込まれます。列名(1行目のヘッダー)がそのままプロパティ名になるので、$data.Nameのようにドット記法でアクセスできるのが便利なところですね。

PowerShell
$data.Name
$data.Department
実行結果
ちゅん一郎
ちゅん次郎
ちゅん三郎
ちゅん狂四郎
ちゅんゴロー

営業部
開発部
営業部
総務部
情報システム部

注意点として、Import-Csvで読み込んだ値はすべて文字列として扱われます。Ageの列も見た目は数字ですが、実際には"41"という文字列になっています。これは後で条件抽出のところで効いてきます。

なお「ちゅんゴロー」のAgeですが、値が空文字ではなく$nullとして読み込まれます。CSVの区切りの数がヘッダーより少ない行があると、足りない分のプロパティは$nullになるという挙動です。こちらも処理を行う際に気を付ける必要があります。

列が多いと一覧表示ではなくリスト表示になることがある

列の数が4つまでだと表形式で表示してくれますが、5つ以上になると下記のような感じで表示が変わります。

読み込むCSV
PostTitle,URL,Category,PostDate,PostImageURL
PowerShell 5.1を7.xに更新する方法【winget・MSI対応】,https://chun-log.jp/powershell-versionup/,powershell,2025年10月26日,https://chun-log.jp/wp-content/uploads/2025/10/eye-catch-powershell-versionup-450x236.webp
Windows 10/11のデスクトップに表示される「この写真についての詳しい情報」を消す方法,https://chun-log.jp/windows-hide-spotlight-detail/,windows,2025年10月28日,https://chun-log.jp/wp-content/uploads/2025/10/eye-catch-windows-hide-spotlight-detail-450x236.png
PowerShellでファイルをリアルタイム監視する方法【tail -f相当を実現】,https://chun-log.jp/powershell-get-content/,powershell,2025年10月30日,https://chun-log.jp/wp-content/uploads/2025/10/eye-catch-powershell-get-content-450x236.webp
実行結果
PostTitle    : PowerShell 5.1を7.xに更新する方法【winget・MSI対応】
URL          : https://chun-log.jp/powershell-versionup/
Category     : powershell
PostDate     : 2025年10月26日
PostImageURL : https://chun-log.jp/wp-content/uploads/2025/10/eye-catch-powershell-versionup-450x236.webp

PostTitle    : Windows 10/11のデスクトップに表示される「この写真についての詳しい情報」を消す方法
URL          : https://chun-log.jp/windows-hide-spotlight-detail/
Category     : windows
PostDate     : 2025年10月28日
PostImageURL : https://chun-log.jp/wp-content/uploads/2025/10/eye-catch-windows-hide-spotlight-detail-450x236.png

PostTitle    : PowerShellでファイルをリアルタイム監視する方法【tail -f相当を実現】
URL          : https://chun-log.jp/powershell-get-content/
Category     : powershell
PostDate     : 2025年10月30日
PostImageURL : https://chun-log.jp/wp-content/uploads/2025/10/eye-catch-powershell-get-content-450x236.webp

この場合、Format-Tableコマンドレットにつなげることで同じような表形式での表示としてくれます。

PowerShell
$data = Import-Csv -Path "C:\work\users.csv"
$data | Format-Table

条件に一致する行を抽出する

Where-Objectコマンドレットで絞り込みます。部署で絞り込んでみます。

PowerShell
$data | Where-Object { $_.Department -eq "営業部" }
実行結果
Name       Department Age
----       ---------- ---
ちゅん一郎 営業部     41
ちゅん三郎 営業部     34

年齢で絞り込む場合は少し注意が必要です。34歳以上だけ表示してみます。

PowerShell
$data | Where-Object { [int]$_.Age -ge 34 }
実行結果
Name       Department Age
----       ---------- ---
ちゅん一郎 営業部     41
ちゅん次郎 開発部     37
ちゅん三郎 営業部     34

先ほど触れたとおり$_.Ageは文字列なので[int]で明示的に数値へ変換してから比較しています。ここを省略して$_.Age -ge 30と書いても動いてしまうことがあるのですが、PowerShellの比較演算子は左辺の型に合わせて右辺を変換する挙動があり、文字列同士の比較になると桁数によっては直感と違う結果になります。実際に試してみると、こうなります。

PowerShell
"9" -ge 10
実行結果
True

数値としては910より小さいのに、文字列比較だと"9"の方が大きいと判定されてしまっています。数値の「じゅう」ではなく文字の「いちぜろ」で判断されてるんでしょうね。数値として比較したい列は必ずキャストする癖をつけておくと安心です。

「ちゅんゴロー」がこの結果から抜けているのも実は要注意ポイントです。Ageが$nullの行を[int]でキャストすると0として扱われるため0 -ge 30Falseになり、結果的に「34歳未満だから対象外」と同じ見た目になってしまいます。本当は年齢が不明なだけなのに集計上は34歳未満の人と区別がつかなくなるので、欠損値が混じったCSVを扱うときは一度Where-Object { $null -eq $_.Age }のような形で抜けている行を洗い出しておくと安全です。

CSVの列を追加・編集する

新しい列を追加する

Select-Objectコマンドレットの計算プロパティを使うと既存の列を保ったまま新しい列を追加できます。

PowerShell
$result = $data | Select-Object *, @{
    Name = "Category"
    Expression = {
        if ([int]$_.Age -ge 30) {
            "シニア"
        } else {
            "ジュニア"
        }
    }
}
$result
実行結果
Name         Department     Age Category
----         ----------     --- --------
ちゅん一郎   営業部         41  シニア
ちゅん次郎   開発部         37  シニア
ちゅん三郎   営業部         34  シニア
ちゅん狂四郎 総務部         31  シニア
ちゅんゴロー 情報システム部     ジュニア

ここでも「ちゅんゴロー」に注目してください。Ageが不明なのに[int]$null0として扱われるせいで機械的に「ジュニア」判定になってしまっています。実年齢が分からないのと30歳未満なのは全く別の話なので、欠損値がある列を条件分岐に使うときはこの記事のように黙って0扱いにしてしまわずif ($null -eq $_.Age) { "不明" }のような分岐を先に入れておいたほうが実務では安全だと思います。

既存の値を書き換える

部署名を変更したいときはForEach-Objectの中でプロパティに直接代入します。今回は「情報システム部」が「システム部」に改名された、という想定で書き換えてみます。

PowerShell
$data | ForEach-Object {
    if ($_.Department -eq "情報システム部") {
        $_.Department = "システム部"
    }
}
$data
実行結果
Name         Department Age
----         ---------- ---
ちゅん一郎   営業部     41
ちゅん次郎   開発部     37
ちゅん三郎   営業部     34
ちゅん狂四郎 総務部     31
ちゅんゴロー システム部

ここでポイントなのが、この書き方は$dataの中身そのものを書き換えている点です。PSCustomObjectは参照型なのでForEach-Object$_$dataに入っているオブジェクトそのものを指しています。なので新しい変数に代入しなくても$dataを確認すればもう「システム部」に変わっています。

ただし、先に作っておいた$resultの方は話が別です。試しに中身を見てみます。

PowerShell
$result
実行結果
Name         Department     Age Category
----         ----------     --- --------
ちゅん一郎   営業部         41  シニア
ちゅん次郎   開発部         37  シニア
ちゅん三郎   営業部         34  シニア
ちゅん狂四郎 総務部         31  シニア
ちゅんゴロー 情報システム部     ジュニア

$resultの「ちゅんゴロー」の部署名は「情報システム部」のままで、システム部には変わっていません。Select-Object *で新しいオブジェクトを作った時点でプロパティの値がコピーされるので、$result$dataとは別物になっています。あとから$data側だけ書き換えても$resultには反映されない、というのは実際にハマりやすいポイントだと思うので、新しい変数に結果を貯めていく派の人は覚えておいて損はないです。

Export-CsvでCSVファイルとして出力する

Export-CsvはPowerShellオブジェクトのプロパティを列、各オブジェクトを行としてCSVファイルへ保存するコマンドレットです。ここでは$result(Category列が付いている方)を出力します。

PowerShell
$result | Export-Csv -Path "C:\work\users_updated.csv" -NoTypeInformation
Get-Content -Path "C:\work\users_updated.csv"
実行結果
"Name","Department","Age","Category"
"ちゅん一郎","営業部","41","シニア"
"ちゅん次郎","開発部","37","シニア"
"ちゅん三郎","営業部","34","シニア"
"ちゅん狂四郎","総務部","31","シニア"
"ちゅんゴロー","情報システム部",,"ジュニア"

「ちゅんゴロー」のAge部分がカンマとカンマの間に何もない状態(,,)になっているのが分かると思います。$nullだった値はCSV上では空文字として出力される、ということですね。

-NoTypeInformationはCSVの1行目に#TYPE System.Management.Automation.PSCustomObjectのような型情報ヘッダーが付くのを防ぐオプションです。実はPowerShell 6以降ではこの型情報を出さないのがすでに既定の動作になっていて、-NoTypeInformation を付けなくても同じ結果になります。ただWindows PowerShell 5.1ではこのヘッダーが既定で付いてしまい、Excelなどの他のツールが誤読する原因になるので、環境を問わず動くスクリプトにしたい場合は明示しておくのが無難です。

同じパスに既存のファイルがある場合ですが、Export-Csvは既定で警告なしに上書きしてくれます。よく「上書きするには-Forceが必要」と説明されているのを見かけるのですが、これは正確には少し違っていて、-Force が必要になるのは対象ファイルが読み取り専用属性になっている場合です。普通に存在するだけのファイルなら-Forceなしでそのまま上書きされます(Microsoft公式ドキュメントのサンプルでも、読み取り専用ファイルに対してだけ-Forceなしだとアクセス拒否のエラーになる例が示されています)。

PowerShell
# 読み取り専用ファイルを上書きしたい場合のみ -Force が必要
$result | Export-Csv -Path "C:\work\users_updated.csv" -NoTypeInformation -Force

逆に上書きされたくない場合は-NoClobberを付けると既存ファイルがあるときにエラーで止めてくれます。

Excelで文字化けする・日本語が消える場合の対処

ここが個人的には一番ハマりやすいところだと思っています。原因を理解せずに「とりあえず-Encoding UTF8を付ける」だけだと、環境によっては直らないどころか悪化することもあるので、順番に整理します。

PowerShellのバージョンで既定のエンコードが違う

大きく分けて2系統のPowerShellが存在していて、それぞれExport-Csv/Import-Csvの既定エンコードが違います。

実行環境Export-Csvの既定エンコード備考
PowerShell 7.x(現行、2026年3月にLTSとなった7.6系が最新)UTF-8(BOMなし)クロスプラットフォーム対応のためUTF-8が既定
Windows PowerShell 5.1(Windowsに標準搭載)ASCII日本語などの2バイト文字は保存できない

Windows PowerShell 5.1で-Encodingを何も指定せずにExport-Csvすると、日本語部分はすべて ? に置き換わって保存されます。これは表示上の文字化けではなく、元の文字情報が失われるデータ消失なので特に注意してください。5.1環境でCSVを出力するスクリプトを書くときは-Encoding UTF8 を明示してください。なお5.1には「BOMなしUTF-8」という選択肢自体がなくUTF8を指定すると自動的にBOM付きで保存されます。

PowerShell
# Windows PowerShell 5.1で日本語を欠落させずに出力する
$result | Export-Csv -Path "C:\work\users_updated.csv" -NoTypeInformation -Encoding UTF8

Windows PowerShell 5.1とPowerShell 7系は共存できます。7系への更新手順や現在のバージョンを確認する方法はこちらの記事で詳しく説明しています。

PowerShell 7.xでExcelに合わせて開きたい場合

PowerShell 7.x側は日本語がいきなり消えることはありませんが、既定のBOMなしUTF-8のままExcelでダブルクリックして開くと、ExcelがUTF-8だと判定できずにShift-JISとして読み込んでしまい文字が意味不明な記号(「陦ィ遉コ縺ゅ↑縺ゥ」のような)に化けることがあります。データが消えているわけではなく見た目だけの問題なのでBOM付きで出力すれば解決します。

PowerShell
# Excelでダブルクリックしても文字化けしないように出力する
$result | Export-Csv -Path "C:\work\users_updated.csv" -NoTypeInformation -Encoding utf8BOM

実際に出力したファイルの先頭バイトを確認すると-Encodingを省略した場合は"Nameの文字コードがそのまま先頭に来ますが、-Encoding utf8BOMを付けた場合はEF BB BFというBOMのバイト列が先頭に追加されているのが確認できました。ExcelはこのBOMを見てUTF-8だと判断してくれます。

読み込み側でも化ける場合

PowerShell 7.4以降でANSI形式のCSVを読む場合は-Encoding Ansiを指定します。PowerShell 7.3以前では-Encoding 932のようにコードページを明示してください。Windows PowerShell 5.1では-Encoding Defaultが現在のANSIコードページを使います。

PowerShell
# PowerShell 7.4以降
$data = Import-Csv -Path "C:\work\data.csv" -Encoding Ansi

# PowerShell 7.3以前
$data = Import-Csv -Path "C:\work\data.csv" -Encoding 932

# Windows PowerShell 5.1
$data = Import-Csv -Path "C:\work\data.csv" -Encoding Default

CSVの各行を使って処理を実行する

CSVを設定ファイル代わりに使って、行ごとに何か処理をさせることもよくあります。ここまでの操作で$dataの部署名は書き換え済みなので、「ちゅんゴロー」の部署は「システム部」になっている状態で実行しています。

PowerShell
$data | ForEach-Object {
    Write-Host "$($_.Name)さんの部署は$($_.Department)です"
}
実行結果
ちゅん一郎さんの部署は営業部です
ちゅん次郎さんの部署は開発部です
ちゅん三郎さんの部署は営業部です
ちゅん狂四郎さんの部署は総務部です
ちゅんゴローさんの部署はシステム部です

この考え方は、ユーザーを一括作成したり複数サーバーへ同じ処理を流したりといった作業にもそのまま応用できます。

よくある質問

CSVの列がうまく分かれない

区切り文字がカンマではない可能性があります。例えばセミコロン区切りのCSV(それはCSVというのか、はさておき・・・)を読み込む場合は-Delimiterで区切り文字にセミコロンを指定して読み込みを行います。

PowerShell
Import-Csv -Path "semi.csv" -Delimiter ";"
実行結果
Name       Department Age
----       ---------- ---
ちゅん一郎 営業部     41
ちゅん次郎 開発部     37

数値の比較結果がおかしい

CSVから読み込んだ値は文字列なので比較の前に数値へキャストしてください。本文の「条件に一致する行を抽出する」で説明したとおりです。

PowerShell
[int]$_.Age

Export-Csvで出力した値が全部ダブルクォートで囲まれてしまう

Export-Csvは既定ですべての値をダブルクォートで囲む仕様になっています。PowerShell 7.0以降であれば-UseQuotes AsNeededを指定すると、区切り文字や改行を含む値だけを囲むようにできます。

出力ファイルを上書きしようとしたらアクセス拒否のエラーになってしまった

対象のCSVファイルが読み取り専用属性になっている可能性があります。-Forceを付けると読み取り専用ファイルでも上書きできます。

CSV操作以外のファイル確認や文字列検索も続けて試す場合はPowerShellの記事一覧から目的別に探せます。

まとめ

  • Import-CsvはCSVの1行を列名付きオブジェクトとして読み込んでくれるよ。値が足りない行はそのプロパティが$nullになる点も覚えておいてね。
  • 条件抽出はWhere-Objectで、数値列は[int]キャストを忘れないでね。ただし$nullは0扱いになるので、欠損値の混じったデータでは注意してね。
  • 列の追加はSelect-Objectの計算プロパティ、既存値の書き換えはForEach-Objectが定番だよ。Select-Objectで作った新しいオブジェクトは元のデータとは別物になる点もポイントだよ。
  • Export-Csvで上書きするのに-Forceは基本いらなくて、読み取り専用ファイルのときだけ必要になるよ。
  • 文字化け・文字消失はPowerShellのバージョンと実際のファイルの文字コードを両方確認してから-Encodingを合わせてね。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次