【问题标题】:How to read a line to a specific point and then add a new line如何读取一行到特定点然后添加新行
【发布时间】:2015-10-30 05:42:13
【问题描述】:

我正在尝试制作一个文件解析器,它将获取一个原始输入文件,然后创建一个新文件,该文件具有正确顺序的所有元素并带有正确的新行,以便另一个解析器可以读取它。

所以我的文件是 PGN(便携式游戏符号)。这些文件在国际象棋中用于记录人们在计算机上玩的游戏。

它们看起来像:

--------------------------------------
[Event "Computer chess game"]
[Date "2015.10.28"]
[Round "?"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
[BlackElo "2400"]
[ECO "A25"]
[Opening "English"]
[Time "10:39:20"]
[Variation "Closed"]
[WhiteElo "2400"]
[Termination "normal"]
[PlyCount "63"]
[WhiteType "human"]
[BlackType "human"]
1. f3 e6 2. g4 Qh4# 1-0
-------------------------------

在 Reddit 的 /r/chess 上,您可以在其中使用 [pgn][/pgn] 围绕您的游戏,然后它会创建一个可玩的棋盘,您或其他人可以在您的游戏中逐步完成并为您提供建议等。

问题是我玩的网站,PGN 出现如上。 /r/chess 解析器不喜欢它。

它希望每个动作都在单独的一行上:

1. f3 e6 
2. g4 Qh4# 1-0

由于我正在尝试学习 Powershell,因此我想创建一个脚本来打开原始 PGN,然后将其重新格式化为如上所示,并可能会提取事件、日期、白色玩家、黑色玩家和结果.然后对其进行格式化,以便在每次移动后插入一个新行。然后输出一个用[pgn][/pgn]包围的新文件。

我对如何做到这一点有点迷茫。我需要使用正则表达式吗?我知道,一旦我将文件读入 Powershell,我也可以将其视为数组。

输出文件应如下所示:

[pgn][Event "Computer chess game"]
[Date "2015.10.28"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
1. f3 e6 
2. g4 Qh4# 1-0 [/pgn]

感谢您的任何帮助!

【问题讨论】:

  • 行总是按这个顺序吗?并且总是相同的计数?

标签: powershell powershell-2.0 powershell-3.0


【解决方案1】:

这是一个功能,它将您的 PGN 转换为与 Reddit 兼容的格式。请注意,目前它不支持每个文件包含多个游戏的 PGN。

使用此功能,您可以:

  • 选择要保留的“标题”。默认情况下,仅保留 EventDateWhiteBlackResult
  • 将新的 PGN 保存到文件或输出到管道

函数接受以下参数:

  • 路径原始 PNG 文件的路径
  • OutFile转换后的 PGN 文件的路径
  • KeepHeaders要保留的“标题”列表

用法示例:

  • 转换文件,输出到屏幕

    ConvertPgn-ForReddit -Path .\Foo.pgn
    
    [pgn]
    [Event "Computer chess game"]
    [Date "2015.10.28"]
    [White "White Player"]
    [Black "Black Player"]
    [Result "1-0"]
    1. f3 e6 
    2. g4 Qh4# 1-0
    [/pgn]
    
  • 转换文件,输出到文件

    ConvertPgn-ForReddit -Path .\Foo.pgn -OutFile .\Bar.pgn
    
  • 转换文件,输出到屏幕,只保留BlackEloTime 标头

    ConvertPgn-ForReddit -Path .\Foo.pgn -KeepHeaders BlackElo, Time
    
    [pgn]
    [BlackElo "2400"]
    [Time "10:39:20"]
    1. f3 e6 
    2. g4 Qh4# 1-0
    [/pgn]
    

代码:

function ConvertPgn-ForReddit
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateScript({
            Test-Path $_
        })]
        [ValidateNotNullOrEmpty()]
        [string]$Path,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string]$OutFile,

        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string[]]$KeepHeaders = @('Event', 'Date', 'White', 'Black', 'Result')
    )

    Process
    {
        # Get file contents as array of strings
        $PgnFile = Get-Content -Path $Path 

        # Get all "headers", e.g. [Event "Computer chess game"]
        $Headers = $PgnFile | Where-Object {$_ -match '\[.*\]'}

        # Filter "headers", so they contain only the ones we want
        $FilteredHeaders = $KeepHeaders | ForEach-Object {
            $currHeader = $_
            $Headers | Where-Object {$_ -match  "\[$currHeader\s+.*\]"}
        }

        # Get chess moves
        $Moves = $PgnFile | Where-Object {$_ -match '^\d+\.'}
        # Split them, remove empty lines if any
        $SplittedMoves = $Moves | ForEach-Object {$_ -split '(\d+\.)'} | Where-Object {$_}
        # Join splitted chess moves: delimeter + actual move. E.g. "1." +  "f3 e6 "
        $JoinedMoves = 0..($SplittedMoves.Count - 1) | ForEach-Object {
            if([bool]!($_ % 2))
            {
                '{0} {1}' -f $SplittedMoves[$_], $SplittedMoves[$_+1]
            }
        }

        # Create PGN in Reddit-compatible format
        $RedditPgn = '[pgn]', $FilteredHeaders, $JoinedMoves, '[/pgn]'

        if($OutFile)
        {
            # If OutFile is specified, save it
            $RedditPgn | Set-Content -Path $OutFile
        }
        else
        {
            # If not - just output to the pipeline
            $RedditPgn
        }
    }
}

【讨论】:

  • 谢谢!这是我目前使用 Powershell 的经验,甚至都不好笑! 9 之后的数字似乎有问题。十是 0,十一是 1,等等。
  • 在解析整个 PGN 之前它似乎也停止了。我有一个可以走68步的。它停在那一步的第 9 步。
  • @JRF2k 是的,在正则表达式中忘记了,现在已修复。试一试:)。
【解决方案2】:

嘿嘿,

编辑:这是不正确的,因为我不知道 PGN 是如何工作的......

这是我的尝试。输入文件pgn.txt,输出文件converted_pgn.txt。根据您提供的样本。如果您更改输入文件中的行数或它们的顺序,它就会分解成碎片:)。

Get-Content 将从提供的输入文件中生成一个array。然后,您只需保留所需的单元格。

为了区分动作,我在空格上选择了Split

所有这些都没有真正完善,如果一切都意味着改变,你可以用正则表达式做得更好,输入明智。

#$pgn = Get-Content "pgn.txt"
#this will give us an array like below

$pgn = "--------------------------------------",
"[Event `"Computer chess game`"]",
"[Date `"2015.10.28`"]",
"[Round `"?`"]",
"[White `"White Player`"]",
"[Black `"Black Player`"]",
"[Result `"1-0`"]",
"[BlackElo `"2400`"]",
"[ECO `"A25`"]",
"[Opening `"English`"]",
"[Time `"10:39:20`"]",
"[Variation `"Closed`"]",
"[WhiteElo `"2400`"]",
"[Termination `"normal`"]",
"[PlyCount `"63`"]",
"[WhiteType `"human`"]",
"[BlackType `"human`"]",
"1. f3 e6 2. g4 Qh4# 1-0",
"-------------------------------"

$moves = $pgn[17].Split(" ")

$m1 = $moves[0] + " " + $moves[1] + " " + $moves[2]

$m2 = $moves[3] + " " + $moves[4] + " " + $moves[5] + " " + $moves[6]

"[pgn]$($pgn[1])",$pgn[2],$pgn[4],$pgn[5],$pgn[6],$m1,"$m2 [/pgn]" | Out-File "converted_pgn.txt"

输出:

[pgn][Event "Computer chess game"]
[Date "2015.10.28"]
[White "White Player"]
[Black "Black Player"]
[Result "1-0"]
1. f3 e6
2. g4 Qh4# 1-0 [/pgn]

【讨论】:

  • 我本以为会有反对意见的评论... :-/
  • 很抱歉。我投了反对票,因为您的答案依赖于 PGN 中仅存在 2 个动作的假设。
  • 您应该删除前几行,因为它们不会对答案添加任何内容。如果您认为没有努力,为什么要投入一些?
  • 完成。我应该选择一个我更了解的领域:)
猜你喜欢
  • 1970-01-01
  • 2021-03-11
  • 2021-12-12
  • 2018-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-02-10
  • 2020-08-24
相关资源
最近更新 更多