Powershell 正则表达式用于德语变音符号,基于大写/小写和字符串中的位置



我正在尝试在Powershell中编写脚本来转换德语变音符号

ä, ö, ü, ß to ae, oe, ue, ss 
Ä, Ö, Ü, ß to AE or Ae, UE or Ue, and SS.

问题是我还需要根据 Umlaut 的位置进行区分。

ÜNLÜ > UENLUE
Ünlü > Uenlue (Ue)
SCHNEEWEIß > SCHNEEWEISS
Schneeweiß > Schneeweiss
Geßl > Gessl
GEßL > GESSL
Josef Öbinger > Josef Oebinger (one string)
Jürgen MÜLLER > Juergen MUELLER (one string)

毁掉我一天的主要问题是变音符号ß

ß 没有大写和小写

我需要根据前一个字符是大写还是小写来识别 ß

我尝试过各种正则表达式,例如[ÄÖÜßA-Z]{1,}(?![sa-zäüö])[ÄÖÜßA-Z][ÄÖÜß][^a-z]

除此之外,像 ÜNLÜ 这样的单词只能用一个变音符号识别,因为带有变音符号的字母位于单词的末尾。

我需要 3 个匹配的正则表达式模式。一个用于大写,一个用于小写,一个用于混合大小写(Oebinger)

然后,这 3 种模式将被放入 powershell 中的 3 个 IF 条件中,然后我可以根据匹配的模式盲目转换。

[ÄÖÜß][^a-z]为ÜNLÜ工作> UENLUE

[äöüß][^A-Z]为尤尔根>于尔根工作

但 Schneeweiß 和 SCHNEEWEIß 中的 ß 与两种模式匹配。这不是我想要的。

我需要一种模式来检查 ß 之前和之后的字母是小写还是大写。 如果小写于 ß = ss,如果大写则 ß = SS

第三种情况,混合情况实际上并不需要单独的正则表达式。我基本上可以使用 String Jürgen MÜLLER,通过两种模式在 powerscript 中运行它。第一个模式将其转换为Jürgen MUELLER。拿这个再跑一遍,得到尤尔根·穆勒。

变音 ß 总是相同的。小写 = 大写。这就是使整个事情如此困难的原因。

我正在失去希望。请帮助我伙计们。

PowerShell (Core) 7+ 提供了一个简洁的解决方案,假设那里的-replace运算符接受脚本块作为替换操作数,从而可以根据找到的每个匹配项实现灵活、动态的替换:

$strings = @(
'ÜNLÜ'           # > UENLUE
'Ünlü'           # > Uenlue (Ue)
'SCHNEEWEIß'     # > SCHNEEWEISS
'Schneeweiß'     # > Schneeweiss
'Geßl'           # > Gessl
'GEßL'           # > GESSL
'Josef Öbinger'  # > Josef Oebinger
'Jürgen MÜLLER'  # > Juergen MUELLER
'THEÖ HÄRSHERIN' # > THEOE HAERSHERIN
'MÄßIG'          # > MAESSIG
)
$strings `
-replace '[äöü](?:(?=ß)|p{L})?', { 
([string] $_.Value[0]).Normalize('FormD')[0] + 
([char]::IsUpper($_.Value[1] ?? $_.Value[0]) ? 'E' : 'e') +
$_.Value[1]
} `
-replace '.ß', { 
$_.Value[0] + ([char]::IsUpper($_.Value[0]) ? 'SS' : 'ss') 
}

注意:

  • 对包含单个变音字符的字符串调用.Normalize('FormD')[0]实际上会将该字符转换为其 ASCII 基本字母;例如,ü变为u- 请参阅System.String.Normalize

在WindowsPowerShell(传统的,仅限Windows的版本,其最新版本是v5.1)中:

  • 您需要直接调用底层 .NET API,即[regex]::Replace()
  • 您还需要使用if语句来代替三元运算符 (<condition> ? <if-true> : <else>) 和空合并运算符 (??),它们也仅在 PowerShell (Core) 7+ 中可用。

因此,解决方案要复杂得多:

$strings | ForEach-Object {
$aux = 
[regex]::Replace(
$_,
'[äöü](?:(?=ß)|p{L})?',
{ 
param($m) 
([string] $m.Value[0]).Normalize('FormD')[0] +
$(if ([char]::IsUpper($(if ($m.Value[1]) { $m.Value[1] } else { $m.Value[0] }))) { 'E' } else { 'e' }) +
$m.Value[1]
},
'IgnoreCase'
)  
[regex]::Replace(
$aux,
'.ß',
{ 
param($m) 
$m.Value[0] + $(if ([char]::IsUpper($m.Value[0])) { 'SS' } else { 'ss' }) 
},
'IgnoreCase'
)  
}

注意:以上是PowerShell(核心)7+解决方案的直接等价物,但第二次[regex]::Replace()调用可以替换为以下内容,如js2010的答案所示:

$aux -creplace '(?<=p{Ll})ß', 'ss' -creplace '(?<=p{Lu})ß', 'SS'

感谢您提出如此有趣的问题!

我看到有两种方法可以解决这个问题。

您当前采用的方法似乎正在尝试在替换字符串中执行此操作。 这可能会起作用,尽管我怀疑您想使用-creplace或明确区分大小写的正则表达式。

我会尝试的方法是使用正则表达式替换评估器。 这些在PowerShell中相当容易,因为你可以将[ScriptBlock]强制转换为任何委托。

我相信这个脚本可以解决问题:

$inputString = @'
ÜNLÜ
Ünlü
SCHNEEWEIß
Schneeweiß
Geßl
GEßL
Josef Öbinger
Jürgen MÜLLER
'@
$UmulatesPattern = [Regex]::New('[ÄÖÜäöüß]')
$UmulatesPattern.Replace($InputString,{
param($match)
$wasCapitalized = $match.Value -cmatch 'p{Lu}'

$lastCharacter = 
if ($match.Index -gt 1) {
$inputString[$match.Index - 1]
} else { ' ' }
$nextCharacter = 
if ($match.Index -lt ($inputString.Length - 2)) {
$inputString[$match.Index + 1]
} else { ' ' }
$shouldCapitalizeAll = 
$lastCharacter -cmatch '[sp{Lu}]' -and
$NextCharacter -cmatch '[sp{Lu}]'

$replacement = 
switch ($match) {
"ä" {"ae"}
"ö" {"oe"}
"ü" {"ue"}
"ß" {"ss"}
}
if ($shouldCapitalizeAll) {
$replacement.ToUpper()
} elseif ($wasCapitalized) {
'' + $replacement.Substring(0,1).ToUpper() + $replacement.Substring(1)
} else {
$replacement
}
})

如上面的答案所示,赋值器有帮助的原因是,赋值器可以轻松进行替换,具体取决于匹配的周围上下文。

运行上面的代码会生成此列表,该列表似乎与您想要的体验一致:

UENLUE
Uenlue
SCHNEEWEISS
Schneeweiss
Gessl
GESSL
Josef Oebinger
Juergen MUELLER

唯一的其他附加说明是,在确定字母配对是否应大写时,我最终使用了前后字符的上下文。

将一些注释放在一起,使用大写和小写字符的后面查找,区分大小写替换:

'SCHNEEWEIß' -creplace '(?<=p{Lu})ß','SS'
SCHNEEWEISS

'Schneeweiß' -creplace '(?<=p{Ll})ß','ss'
Schneeweiss

'ß' -cmatch 'p{Ll}'  # lower case
True

最新更新