web-dev-qa-db-ja.com

Windowsでのファイル名エンコーディングの決定と変更

名前に特定のアクセント付き文字が含まれているファイルがWindowsサーバーにあります。 Windowsエクスプローラーでは、ファイルは正常に表示されますが、コマンドプロンプトで「dir」を実行すると、デフォルト設定で置換文字が表示されます。

たとえば、文字öはリストにo"として表示されます。これにより、SMBを介して他のプラットフォームからこれらのファイルにアクセスするときに、おそらくエンコード/コードページの競合が原因で問題が発生します。問題はすべてのファイルに存在するわけではなく、問題のあるファイルがどこから来たのかわかりません。

例:

E:\folder\files>dir
 Volume in drive E is data
 Volume Serial Number is 5841-C30E

 Directory of E:\folder\files  

07/05/2016  07:46 PM    <DIR>          .
07/05/2016  07:46 PM    <DIR>          ..
12/01/2015  11:12 AM            14,105 file with o" character.xlsx
01/22/2015  05:30 PM            11,598 file with correct ö character.xlsx
               2 File(s)         25,703 bytes
               2 Dir(s)  2,727,491,600,384 bytes free

ファイル名とディレクトリ名を変更しましたが、わかります。

名前がこのように取得された可能性があるアイデアはありますか?おそらく、別のプラットフォームまたはツールを使用してコピーまたは作成されたのでしょうか。

すべての問題のあるファイルをバッチで見つけて名前を変更するにはどうすればよいですか? GUIの名前変更ユーティリティをいくつか調べましたが、問題は発生せず、Windowsエクスプローラーに表示されている名前でしか機能しません。

ドライブ上のファイルシステムはReFSですが、それは何か関係があるのでしょうか?

編集:PowerShellコマンドを実行しました

Y:\test>powershell -c Get-ChildItem ^|ForEach-Object {$x=$_.Name; For ($i=0;$i
-lt $x.Length; $i++) {\"{0} {1} {2}\" -f $x,$x[$i],[int]$x[$i]}}
file with o¨ character.xlsx o 111
file with o¨ character.xlsx ¨ 776

関連する部分のみを表示するようにクリーンアップされました。

したがって、実際にはcombining diaeresisであり、垂直引用符ではないように見えます。私が理解しているように、ユニコードの正規化について話すときはそうあるべきです。

2
nixer

JosefZのスクリプトに基づいて、再帰的に機能する変更バージョンを次に示します。

Get-ChildItem "X:\" -Recurse | ForEach-Object {
    $y = $_.Name.Normalize("FormC")
    $file = $_.Fullname
    if ( $y.Length -ne $_.Name.Length ) {
        Rename-Item -LiteralPath "$file" -NewName "$y" -WhatIf
        Write-Host "renamed file $file"
    }
}

削除する -WhatIfテスト後。パスが長すぎるという問題がありましたが、それは別の投稿のトピックです。

0
nixer

次の簡単なPowershellスクリプトを使用して問題を再現できます

$RatedName = "šöü"                            # set sample string
$FormDName = $RatedName.Normalize("FormD")    # its Canonical Decomposition
$FormCName = $FormDName.Normalize("FormC")    #     followed by Canonical Composition
                                              # list each string character by character
($RatedName,$FormDName,$FormCName) | ForEach-Object {
    $charArr = [char[]]$_ 
    "$_"      # display string in new line for better readability
              # display each character together with its Unicode codepoint
    For( $i=0; $i -lt $charArr.Count; $i++ ) { 
        $charInt = [int]$charArr[$i]
        # next "Try-Catch-Finally" code snippet adopted from my "Alt KeyCode Finder"
        #                                       http://superuser.com/a/1047961/376602
        Try {    
            # Get-CharInfo module downloadable from http://poshcode.org/5234
            #        to add it into the current session: use Import-Module cmdlet
            $charInt | Get-CharInfo |% {
                $ChUCode = $_.CodePoint
                $ChCtgry = $_.Category
                $ChDescr = $_.Description
            }
        }
        Catch {
            $ChUCode = "U+{0:x4}" -f $charInt
            if ( $charInt -le 0x1F -or ($charInt -ge 0x7F -and $charInt -le 0x9F)) 
                 { $ChCtgry = "Control" } else { $ChCtgry = "" }
            $ChDescr = ""
        }
        Finally { $ChOut = $charArr[$i] }
        "{0} {1,-2} {2} {3,5} {4}" -f $i, $charArr[$i], $ChUCode, $charInt, $ChDescr
    }
}
# create sample files
$RatedName | Out-File "D:\test\1097217Rated$RatedName.txt" -Encoding utf8
$FormDName | Out-File "D:\test\1097217FormD$FormDName.txt" -Encoding utf8
$FormCName | Out-File "D:\test\1097217FormC$FormCName.txt" -Encoding utf8


""                                 # very artless draft of possible solution
Get-ChildItem "D:\test\1097217*" | ForEach-Object {
    $y = $_.Name.Normalize("FormC")
    if ( $y.Length -ne $_.Name.Length ) {
        Rename-Item -NewName $y -LiteralPath $_ -WhatIf
    } else {
        "       : file name is already normalized $_"
    }
}

上記のスクリプトは次のようにupdatedです:1stはcomposed /に関する詳細情報を示しています分解されたUnicode文字、つまりそれらのUnicode名( Get-CharInfoモジュール を参照)。 2番目埋め込み可能な解決策の非常に芸術的なドラフト
出力from cmdプロンプト:

==> powershell -c D:\PShell\SU\1097217.ps1
šöü
0 š  U+0161   353 Latin Small Letter S With Caron
1 ö  U+00F6   246 Latin Small Letter O With Diaeresis
2 ü  U+00FC   252 Latin Small Letter U With Diaeresis
šöü
0 s  U+0073   115 Latin Small Letter S
1 ̌  U+030C   780 Combining Caron
2 o  U+006F   111 Latin Small Letter O
3 ̈  U+0308   776 Combining Diaeresis
4 u  U+0075   117 Latin Small Letter U
5 ̈  U+0308   776 Combining Diaeresis
šöü
0 š  U+0161   353 Latin Small Letter S With Caron
1 ö  U+00F6   246 Latin Small Letter O With Diaeresis
2 ü  U+00FC   252 Latin Small Letter U With Diaeresis

       : file name is already normalized D:\test\1097217FormCšöü.txt
What if: Performing the operation "Rename File" on target "Item: D:\test\1097217
FormDšöü.txt Destination: D:\test\1097217FormDšöü.txt".
       : file name is already normalized D:\test\1097217Ratedšöü.txt

==> dir /b D:\test\1097217*
1097217FormCšöü.txt
1097217FormDšöü.txt
1097217Ratedšöü.txt

実際、上記のdir出力lookscmdウィンドウと私のUnicode対応ブラウザの1097217FormDsˇo¨u¨.txtのように 文字列を構成 リストされているように上記ですが nicodeアナライザー 文字と最新の画像が表示されます:

combining accents

ただし、次の例では、問題を全幅で示しています。forループがcombiningアクセントをnormalアクセントに変更します。

==> for /F "delims=" %G in ('dir /b /S D:\test\1097217*') do @echo %~nxG & dir /B %~fG
1097217FormCšöü.txt
1097217FormCšöü.txt
1097217FormDsˇo¨u¨.txt
File Not Found
1097217Ratedšöü.txt
1097217Ratedšöü.txt

==>

これが可能な解決策の非常にアートレスドラフトです(上記の出力を参照):

""                                 # very artless draft of possible solution
Get-ChildItem "D:\test\1097217*" | ForEach-Object {
    $y = $_.Name.Normalize("FormC")
    if ( $y.Length -ne $_.Name.Length ) {
        Rename-Item -NewName $y -LiteralPath $_ -WhatIf
    } else {
        "       : file name is already normalized $_"
    }
}

ToDo:必要な場合にのみRename-Itemを呼び出します):

Get-ChildItem "D:\test\1097217*" | ForEach-Object {
    $y = $_.Name.Normalize("FormC")
    if ($true) {                                         ### ToDo
        Rename-Item -NewName $y -LiteralPath $_ -WhatIf
    }
}

とその出力 (ここでも、レンダリングされます 構成された文字列 そして下の画像はcmdウィンドウが偏っていないように見えることを示しています):

What if: Performing the operation "Rename File" on target "Item: D:\test\1097217
FormCšöü.txt Destination: D:\test\1097217FormCšöü.txt".
What if: Performing the operation "Rename File" on target "Item: D:\test\1097217
FormDšöü.txt Destination: D:\test\1097217FormDšöü.txt".
What if: Performing the operation "Rename File" on target "Item: D:\test\1097217
Ratedšöü.txt Destination: D:\test\1097217Ratedšöü.txt".

combining accents

更新されたcmd出力

updated cmd output

1
JosefZ

問題は、地域コントロールパネルのこのタブで発生します。

enter image description here

これは、画面のフォントだけでなく、ファイルシステムにも影響します(基本的には、説明した方法で)。

スクリーンショットは私のマシンのものです。ロケールを英語に変更すると、ľôščťžファイル名のようなすべての特別なスロバキアの国民文字はゴミになりますが、ファイルを開くのを完全に妨げるものもあります(テスト済み... )回避策なし(コードページが元に戻るまで)。ただし、この問題は、多くの言語で見られるáíéのようなより一般的な国の文字では発生しません。

これは、一部のオフラインメディアにも影響します。異なるロケールで作成されたバックアップを開こうとしたとき。

最も簡単な解決策は、リソースにアクセスするすべてのマシンで同じロケールを維持することです。

回避策は、ロケールが異なるマシンを特定し、そのマシンからすべての国の文字の一括置換を実行することです(例:č-> cž-> z)すべてのファイル名。 Total Commander(ファイルマネージャー)は、ディレクトリツリー全体でこのような各ペアの置換を一度に実行できます。次に、そのマシンを英語に戻すか(独自のバックアップを読み取れない場合があることに注意してください)、ファイル名に国別文字を使用しないようにユーザーに要求して、そのままにしておくことができます。

(それでも前に、1つのことを試すことができます。その異なるロケールのマシンでchcpを実行し、使用されているコードページ(たとえば852)を学習してから、chcp 852を使用して他のマシンで試すことができます。これで問題が十分に解決するかどうかはわかりません。)

0
miroxlav