5

У меня есть несколько файлов с именами, содержащими различные символы Unicode. Я хотел бы переименовать их, чтобы они содержали только "печатаемые" символы ASCII (32-126).

Например,

Läsmig.txt         //Before
L_smig.txt         //After
Mike’s Project.zip 
Mike_s Project.zip 

Или для получения бонусных баллов транскрибируй ближайшему персонажу

Läsmig.txt
Lasmig.txt
Mike’s Project.zip
Mike's Project.zip

Идеально ищет ответ, который не требует сторонних инструментов. (Изменить: сценарии приветствуются; я просто пытаюсь избежать нишевых условно-бесплатных приложений, которые должны быть установлены для работы)


Фрагмент оболочки Power, который находит интересующие меня файлы:

gci -recurse | где {$ _.Name -match "[^\u0020-\u007E]"}

Без ответа похож питон вопрос - https://stackoverflow.com/questions/17870055/how-to-rename-a-file-with-non-ascii-character-encoding-to-ascii

2 ответа2

2

Я верю, что это будет работать ...

$Files = gci | where {$_.Name -match "[^\u0020-\u007F]"}

$Files | ForEach-Object {
$OldName = $_.Name
$NewName = $OldName -replace "[^\u0020-\u007F]", "_"
ren $_ $NewName
}

У меня нет такого диапазона имен файлов ASCII для проверки, хотя.

1

Я нашел похожую тему здесь на переполнение стека.

С помощью следующего кода большинство символов будут переведены в "ближайший символ". Хотя я не мог получить в переводе. (Может быть, я не могу сделать имя файла в приглашении с ним;) ß также не переводится.

function Remove-Diacritics {
param ([String]$src = [String]::Empty)
  $normalized = $src.Normalize( [Text.NormalizationForm]::FormD )
  $sb = new-object Text.StringBuilder
  $normalized.ToCharArray() | % {
    if( [Globalization.CharUnicodeInfo]::GetUnicodeCategory($_) -ne [Globalization.UnicodeCategory]::NonSpacingMark) {
      [void]$sb.Append($_)
    }
  }
  $sb.ToString()
}

$files = gci -recurse | where {$_.Name -match "[^\u0020-\u007F]"}
$files | ForEach-Object {
  $newname = Remove-Diacritics $_.Name
  if ($_.Name -ne $newname) {
    $num=1
    $nextname = $_.Fullname.replace($_.Name,$newname)
    while(Test-Path -Path $nextname)
    {
      $next = ([io.fileinfo]$newname).basename + " ($num)" + ([io.fileinfo]$newname).Extension
      $nextname = $_.Fullname.replace($_.Name,$next)
      $num+=1
    }
    echo $nextname
    ren $_.Fullname $nextname
  }
}

Редактировать:

Я добавил код, чтобы проверить, существует ли имя файла, и добавил (1) , (2) т.д., Если он есть. (Он недостаточно умен, чтобы обнаружить уже существующее (1) в имени файла, которое нужно переименовать, так что в этом случае вы получите (1) (1) . Но как всегда ... все программируется;)

Изменить 2:

Вот последний на сегодня ...

У этого есть другая функция для замены символов. Также добавлена строка для замены неизвестных символов, таких как ß и например, на _ .

function Convert-ToLatinCharacters {
param([string]$inputString)
  [Text.Encoding]::ASCII.GetString([Text.Encoding]::GetEncoding("Cyrillic").GetBytes($inputString))
}

$files = gci -recurse | where {$_.Name -match "[^\u0020-\u007F]"}
$files | ForEach-Object {
  $newname = Convert-ToLatinCharacters $_.Name
  $newname = $newname.replace('?','_')
  if ($_.Name -ne $newname) {
    $num=1
    $nextname = $_.Fullname.replace($_.Name,$newname)
    while(Test-Path -Path $nextname)
    {
      $next = ([io.fileinfo]$newname).basename + " ($num)" + ([io.fileinfo]$newname).Extension
      $nextname = $_.Fullname.replace($_.Name,$next)
      $num+=1
    }
    echo $nextname
    ren $_.Fullname $nextname
  }
}

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