Como removo caracteres não ascii de nomes de arquivos?

5

Eu tenho vários arquivos com nomes contendo vários caracteres Unicode. Eu gostaria de renomeá-los para conter apenas os caracteres ASCII "imprimíveis" (32-126).

Por exemplo,

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

Ou para pontos de bônus, transcreva para o personagem mais próximo

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

O ideal é procurar uma resposta que não exija ferramentas de terceiros. (Edit: Scripts encorajados; estou apenas tentando evitar aplicativos shareware de nicho que precisam ser instalados para funcionar)

Power snippet que encontra os arquivos nos quais estou interessado em renomear:

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

Pergunta python semelhante não respondida - link

    
por RJFalconer 24.08.2013 / 21:06

2 respostas

1

Encontrei um tópico semelhante aqui no Stack Overflow.

Com o código a seguir, a maioria dos caracteres será traduzida para o "caractere mais próximo". Embora não consegui obter o traduzido. (Talvez sim, não consigo criar um nome de arquivo no prompt com ele;) O ß também não é traduzido.

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
  }
}

Editar:

Eu adicionei algum código para verificar se um nome de arquivo já existe e adicione (1) , (2) etc ... se isso acontecer. (Não é inteligente o suficiente para detectar um (1) já existente no nome do arquivo a ser renomeado, então nesse caso você obteria (1) (1) . Mas como sempre ... tudo é programável;)

Editar 2 :

Aqui está o último para esta noite ...

Este tem uma função diferente para substituir os caracteres. Também foi adicionada uma linha para alterar caracteres desconhecidos, como ß e , por exemplo, para _ .

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
  }
}
    
por 29.11.2013 / 23:12
2

Eu acredito que isso funcionará ...

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

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

Eu não tenho esse intervalo de nomes de arquivos ASCII para testar.

    
por 24.08.2013 / 21:34