如何读取一行到特定点然后添加新行

How to read a line to a specific point and then add a new line

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

所以我的文件是 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]

感谢任何帮助!

嗨呀,

编辑:这是不正确的,因为我不知道 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 转换为 Reddit-compatible 格式的函数。请注意,目前它不支持每个文件包含多个游戏的 PGN。

使用此功能,您可以:

  • 选择 "headers" 要保留的内容。默认情况下仅保留 EventDateWhiteBlackResult
  • 将新的 PGN 保存到文件或输出到管道

函数接受以下参数:

  • 路径: 原始 PNG 文件的路径
  • OutFile: 转换后的 PGN 文件的路径
  • KeepHeaders要保留的 "headers" 列表

使用示例:

  • 转换文件,输出到屏幕

    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 headers

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