1

У меня есть файлы на сервере 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

Я изменил имена файлов и каталогов, но вы поймете идею.

Любые идеи, как имена могли получить таким образом? Возможно, они были скопированы или созданы с использованием другой платформы или инструмента?

Как я могу найти и переименовать все проблемные файлы? Я посмотрел на несколько утилит переименования графического интерфейса, но они не видят проблему и работают только с именем, показанным в проводнике 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 а не вертикальная кавычка. Как и должно быть, как я понимаю, когда речь идет о нормализации юникода.

3 ответа3

1

Я могу воспроизвести вашу проблему, используя следующий простой скрипт 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 $_"
    }
}

Вышеуказанный скрипт обновляется следующим образом: 1-й показывает больше информации о составленных / разложенных символах Unicode, то есть их именах Unicode (см. Модуль Get-CharInfo); 2-й встроенный очень бесхитростный набросок возможного решения.
Вывод из командной строки 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 выглядит как 1097217FormDsˇo¨u¨.txt в окне cmd и мой браузер, поддерживающий юникод, создает строки, как указано выше, но анализатор юникода показывает символы как на самом деле, так и на последнем изображении:

сочетая акценты

Однако следующий пример показывает проблему во всей ее ширине: изменения цикла for объединяют акценты с обычными :

==> 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".

сочетая акценты

Обновлен вывод cmd

обновлен вывод cmd

0

Проблема возникает в этой вкладке панели управления региона :

Это влияет не только на экранные шрифты, но и на файловую систему (в основном так, как вы описали).

Скриншот с моей машины. Если бы я изменил локаль на английский, все специальные словацкие национальные символы, такие как ľôščťž в именах файлов , станут мусором, в то время как некоторые из них даже полностью запретят открывать файл (проверено ...) без обходного пути (пока кодовая страница не будет возвращена ). Однако эта проблема не появляется с более общими национальными символами , как áíé , которые можно увидеть на многих языках.

Это также влияет на некоторые автономные носители, например, при попытке открыть резервную копию, созданную в другой локали.

Самое простое решение - сохранить одинаковый языковой стандарт на всех машинах, обращающихся к ресурсу

Обходной путь состоит в том, чтобы определить, какая машина имеет другую локаль, и с этой машины выполнить массовую замену всех национальных символов (например, č -> c , ž -> z) во всех именах файлов. Total Commander (файловый менеджер) может выполнять замену каждой такой пары сразу во всем дереве каталогов. Затем вы можете вернуть эту машину на английский (будьте осторожны, возможно, она не сможет читать свои собственные резервные копии) или оставить ее как есть, попросив пользователей не использовать национальные символы в именах файлов.

(Тем не менее, до этого вы можете попробовать одну вещь: вы можете запустить chcp на машине с этим другим языком, узнать, какая кодовая страница используется (например, 852), а затем попробовать на других машинах с chcp 852 . Не уверен, что это удовлетворительно решит проблему.)

0

Основанная на скрипте 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 после тестирования. У меня были проблемы с путями, которые были слишком длинными, но это тема для другого поста.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .