将包含一列的文本文件转换为多列

时间:2016-01-07 16:34:58

标签: powershell batch-file scripting

我有一个包含一种列格式数据的文件。我需要将此文件用作输入文件,输出文件应采用多列格式。我需要一个可以进行转换的脚本的帮助。无论是PowerShell还是批处理都无关紧要。

输入文件内容:input.txt

商店1:
苹果
橙色

结束

商店2:

园内


结束

store3:
大厦


店内
杂货店
结束

输出文件应为:
商店1:1,商店:,store3:
苹果,树,建筑物 橙色,公园,道路 桃子,池塘,桃子 ,鸟,商店
,,杂货店

4 个答案:

答案 0 :(得分:2)

我知道这是一个噱头,但我把它当作自己的学习机会,因为我有代码也许其他人可以从中学习

$text = gc C:\temp\input.txt
$groups = ($text | out-string) -split 'the end' | ? {$_ -notmatch '^(?:\s+)?$'}
$columns = $groups | % {$_.trim().split("`n")[0]}
$rows = $groups | % {$_.trim().Split("`n").count - 2} | sort -desc | select -f 1

$result = 0..$rows | % {
    $row = $_
    $obj = New-Object psobject
    0..$($columns.Count-1) | % {
        $column = $columns[$_]
        $store = $groups[$_].trim().split("`n")
        $item = $store[$row+1]
        $obj | Add-Member -MemberType NoteProperty -Name $column.trim() -Value $(if ($item) {$item.trim()})
    }
    $obj
}

$result | epcsv C:\temp\input.csv -NoTypeInformation

答案 1 :(得分:0)

这是一个纯粹的解决方案:

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem Define global settings here:
set "INFILE=input.txt"
set "OUTFILE=output.txt"
set "HEAD=^store[1-9][0-9]*:$"
set "FOOT=^THE END$"
set "DELIM=,"

set /A "COL=0, ROW=0, MAX=0"
for /F "delims=" %%L in ('
    findstr /N /R "^" "%INFILE%"
') do (
    set "LINE=%%L"
    setlocal EnableDelayedExpansion
    set "LINE=!LINE:*:=!"
    if defined HEAD (
        if !COL! EQU 0 set /A "ROW=-1"
        cmd /V /C "echo^(!LINE!"| > nul findstr /R /C:"%HEAD%" ^
            && if !ROW! LSS 0 set /A "COL+=1, ROW=0"
        if defined FOOT (
            cmd /V /C "echo^(!LINE!"| > nul findstr /R /C:"%FOOT%" ^
                && set /A "ROW=-1" || if !COL! GTR 0 if !ROW! GEQ 0 set /A "ROW+=1"
        ) else (
            if !COL! GTR 0 set /A "ROW+=1"
        )
    ) else (
        if defined FOOT (
            if !ROW! EQU 0 set /A "COL+=1"
            cmd /V /C "echo^(!LINE!"| > nul findstr /R /C:"%FOOT%" ^
                && set /A "ROW=0" || set /A "ROW+=1"
        ) else (
            if !COL! EQU 0 set /A "COL=1"
            if defined LINE (
                set /A "ROW+=1"
            ) else (
                if !ROW! GTR 0 set /A "COL+=1"
                set /A "ROW=0"
            )
        )
    )
    if !MAX! LSS !ROW! set /A "MAX=!ROW!"
    for /F "tokens=1-3 delims=;" %%I in ("!COL!;!ROW!;!MAX!") do (
        endlocal
        if %%I GTR 0 if %%J GTR 0 (
            set "COLLECT[%%I_%%J]=%%L"
        )
        set /A "COL=%%I, ROW=%%J, MAX=%%K"
    )
)
setlocal EnableDelayedExpansion
> "%OUTFILE%" (
    for /L %%J in (1,1,%MAX%) do (
        set "LINE="
        for /L %%I in (1,1,%COL%) do (
            if %%I GTR 1 set "LINE=!LINE!!DELIM!"
            if defined COLLECT[%%I_%%J] (
                set "LINE=!LINE!!COLLECT[%%I_%%J]:*:=!"
            )
        )
        echo(!LINE!
    )
)
endlocal
endlocal
exit /B

这个脚本基本上收集类似数组的变量COLLECT[COL_ROW]中的数据,其中COLROW分别表示列和行索引。代码由两个循环组成,第一个循环遍历给定的输入文件并将行文本分配给相关的数组元素。预定义的页眉和页脚字符串(或者,如果两者都未提供,则为任何空行)控制适用的COLROW索引的确定。 MAX保存最大的行索引ROW,因为数据块的大小可能不同,以便以后填充。第二个循环枚举收集的数据数组,为每列构建一行文本并将它们写入指定的输出文件。

标记为rem的开头的代码部分定义了脚本的全局设置,例如输入文件(INFILE),输出文件(OUTFILE),页眉和页脚分别为HEADFOOT;两个findstr - 兼容的正则表达式;其中一个或两个都可以为空)和分隔符(DELIM)。

这种方法有4种模式:

  1. 页眉和页脚都是非空的:

    • 从第一个标题字符串开始收集数据;
    • 页脚启动新数据列后出现的另一个标题;
    • 页脚和下一个页眉之间的所有内容都会被忽略;
    • 一个标题出现在另一个之后,并且在页脚被视为普通字段之前;
    • 标题文本包含在返回的数据中,页脚文本不包含在内;
    • 保留空行,这意味着它们会产生一个空字段;
  2. 标头非空,但页脚为空:

    • 从第一个标题字符串开始收集数据;
    • 另一个标题会启动一个新数据列;
    • 标题文本包含在返回的数据中;
    • 保留空行,这意味着它们会产生一个空字段;
  3. 页脚非空,但标题为空:

    • 从第一行开始收集数据;
    • 页脚启动新数据列;
    • 页脚文本不包含在返回的数据中;
    • 保留空行,这意味着它们会产生一个空字段;
  4. 页眉和页脚都是空的:

    • 从第一个非空行开始收集数据;
    • 一个或多个连续空行的块启动新数据列;
    • 空行不包含在返回的数据中;
  5. 注意:
    虽然问题缺乏海报的信息和尝试或研究,但我决定回答这个问题,因为手头的任务是一个非常有趣的挑战,用来解决。

    编辑:以下代码是来自用户Aacini(不是来自aschipfl)的解决方案,此答案的原始海报给予了他的亲切许可。我被迫这样做,因为问题已经结束,我真的想发布我的代码!

    @echo off
    setlocal EnableDelayedExpansion
    
    rem Initialize data for first store
    set /A max=0, lines=0, store=0
    
    for /F "delims=" %%a in (input.txt) do (
       if "%%a" neq "THE END" (
          rem Process the next line of this store
          set /A lines+=1
          for %%l in (!lines!) do (
             if not defined line[%%l] (
                rem This store have more lines than previous ones: initialize new line
                for /L %%i in (1,1,!store!) do set "line[%%l]=!line[%%l]! ,"
             )
             rem Append new data to this line
             set "line[%%l]=!line[%%l]!%%a,"
          )
       ) else (
          rem This store ends: get the maximum number of lines
          if !lines! gtr !max! (
             set "max=!lines!"
          ) else (
             rem Enlarge the additional lines of previous stores, if any
             set /A lines+=1
             for /L %%i in (!lines!,1,!max!) do set "line[%%i]=!line[%%i]! ,"
          )
          rem Pass to next store
          set /A lines=0, store+=1
       )
    )
    
    rem Output all result lines
    (for /L %%i in (1,1,%max%) do echo !line[%%i]:~0,-1!) > output.txt
    

    输出:

    store1:,store2:,store3:
     apple, Tree, Building
     orange, Park, Road
     peach, Pond, peach
     , Bird, store
     , , Grocery
    

答案 2 :(得分:-1)

您可以将文本文件传输到此PowerShell脚本中。它使用PowerShell的CSV方言(包括引用字符)。

Begin {
    # corresponds to (untransposed) records
    $records = @()
    # the current record
    $this_record = @()
    # maximum fields of any (untransposed) record
    $max_fields = 0
}
Process {
    If ($_ -eq "THE END") {
        # Append the record to the array.
        $records += ,$this_record

        # Count the maximum number of fields (this will be the number of
        #   records when the data is transposed).
        If ($this_record.Length -gt $max_fields) {
            $max_fields = $this_record.Length
        }

        $this_record = @()
    } ElseIf ($_.Trim() -eq "") {
        # Ignore blank lines.
    } Else {
        # Append the field to the current record.
        $this_record += $_
    }
}
End {
    # Transpose the fields
    $objects = @()
    For ($col=0; $col -lt $max_fields; $col+=1) {
        # ConvertTo-CSV gets object properties. It doesn't implicitly
        #   operate on arrays the way we'd prefer.
        $obj = New-Object PSCustomObject
        For ($row=0; $row -lt $records.Length; $row+=1) {
            # Create property names that sort lexically (zero-padded numbers).
            $obj | Add-Member -MemberType NoteProperty `
                          -Name ("{00000}" -f $row) `
                          -Value $records[$row][$col]
        }
        $objects += $obj
    }

    # Convert to CSV, throw away the header
    $objects | ConvertTo-CSV -NoTypeInformation | Select-Object -Skip 1
}

E.g。 PowerShell -NoProfile -ExecutionPolicy Bypass -File xpose.ps1 < input.txt生成:

"store1:","store2:","store3:"
"apple","Tree","Building"
"orange","Park","Road"
"peach","Pond","peach"
,"Bird","store"
,,"Grocery"

答案 3 :(得分:-1)

这里有一些代码可以帮到你。研究这个!

appendcolumn.bat

@echo off

set i=1
for /f "tokens=*" %%x in ('more') do (
    call :app !i! %%x
    set /a i += 1
)
exit /b 

:app
set line%1=!line%1!,%2
exit /b