【问题标题】:Compare two columns in two different txt files windows比较两个不同 txt 文件窗口中的两列
【发布时间】:2014-11-21 10:35:19
【问题描述】:

我有两个非常大的 .txt 文件(约 50 万行)。我需要从两个文件中取出两列(按列名)并将它们相互比较(类似于 LEFT JOIN 在 SQL 中的工作方式)。因此,我需要将第一个文件中两列中不存在于第二个文件中的所有值组合输出到第三个 txt/csv 文件。

我需要自动化这个过程,所以我应该能够从命令行调用它。如果有人能指出我正确的方向,我将不胜感激。

更新 文件的格式完全相同,所需的列永远不会为空。

示例

第一个文件

数据源;顾客;城市;映射;苏格集团
艺术;约翰;伦敦;强尼;伦敦客户
艺术;克里斯;慕尼黑;乔恩斯;德国
联邦调查局;玛丽;伦敦;詹姆士;德国

第二个文件

数据源;顾客;城市;映射;苏格集团
艺术;克里斯;慕尼黑;乔恩斯;德国
联邦调查局;玛丽;伦敦;詹姆士;德国

我需要做的是获取两列:客户和映射。并找到第一个文件中而不是第二个文件中的行。因此,在给定的示例中,输出文件如下所示:

输出文件:

客户;映射
约翰;强尼

【问题讨论】:

  • 创建数据库,将字符串添加到表中并执行 LEFT JOIN? ;)
  • @duDE 已经在这样做了。由于客户端环境的问题,速度很慢。所以试着想出别的东西。
  • 您可以使用Import-Csv 轻松导入文件,然后只选择您需要的列到compare-Object。假设您的文件是分隔的,那是当然的,否则您将不得不手动分隔它们。然而,内存中的 100 万行数据对于 powershell 来说可能很困难
  • 您当然知道 cmd 本质上是 s-l-o-w 。也许可以做到 - 尝试给我们一个小样本(只有几行,最好带有标题。)然后准备其他问题,例如分隔符是什么,可能有多少独特的组合,是否有空列,是这实际上是固定列格式吗?您希望能够按名称选择列还是仅限于已知列?这两个文件的布局是否相同?哦 - 一些你想做的事情的例子将是无价的。

标签: windows powershell text cmd


【解决方案1】:

我建议不要使用Import-CSV,因为它不适用于 100+ Mb 范围内的文件。嗯,它有效,但速度很慢。

创建一个哈希表。逐行读取第二个文件。连接两列并将结果存储在哈希表中。逐行读取第一个文件并连接其两列以获得相似的键。检查哈希表是否包含相同的键。如果没有,请将数据保存到第三个文件中。

对于代码示例,请提供示例输入和所需的输出。

更新:

您没有指定是否可能有相同的客户,映射但其他数据发生变化。假设不是这样,只需像这样计算整行的哈希,

# Arraylist's initial size 500,000 elemnents
$secondFile = new-object Collections.ArrayList(500000)
# Init MD5 tools
$md5 = new-object Security.Cryptography.MD5CryptoServiceProvider
$utf8 = new-object Text.UTF8Encoding
# Read the 2nd large file
$reader = [IO.File]::OpenText("c:\temp\secondFileBig.txt")
$i=0
while( ($line = $reader.ReadLine()) -ne $null) {
    # Get MD5 for each row and store it in the arraylist
    $hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($line)))
    $secondFile.Add($hash) | out-null
    if(++$i % 25000 -eq 0) {write-host -nonewline "."}
}
$reader.Close()
# Sort the arraylist so that it can be binarysearched
$secondFile.Sort()

通过使用一些大约 500,000 行的虚拟数据,在我的计算机上创建散列需要大约 50 秒。现在,让我们读取另一个文件并逐行检查它是否具有相同的内容。

# Open and read the file row-vise
$reader = [IO.File]::OpenText("c:\temp\firstFileBig.txt")

while( ($line = $reader.ReadLine()) -ne $null) {
    # Get MD5 for current row
    $hash = [System.BitConverter]::ToString($md5.ComputeHash($utf8.GetBytes($line)))
    # If the row already exists in the other file, you'd find its MD5 index with
    # binarysearch in O(n log n) time. If found, you'd get zero or larger index        
    if($secondFile.BinarySearch($hash) -le -1) {
        "Not found: $line"
    }
}
$reader.Close()

使用虚拟测试数据运行第二部分要快得多,可以通过Measure-Command 找到。留给读者作为练习来弄清楚如何提取相关元素。

【讨论】:

  • 非常感谢您的回复。我添加了一个例子
  • 非常感谢您的更新。真的很感激。我今天会试一试,然后回来找你。
  • 再次感谢您的帮助。你的建议很有帮助。虽然不幸的是由于客户端环境的安全限制,我还是写了一个小的 c# 控制台应用程序路由。
【解决方案2】:
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=."
(
FOR /f "skip=1tokens=2,4delims=;" %%a IN (q26059159-2.txt) DO ECHO(%%a; %%b
)>q26059159-temp.txt
(
FOR /f "tokens=2,4delims=;" %%a IN (q26059159-1.txt) DO (
 ECHO(%%a; %%b|FINDSTR /v /x /g:q26059159-temp.txt>NUL
 IF NOT ERRORLEVEL 1 (
  SET "col1=%%a;%%b"
  ECHO(!col1:~1!
 )
)
)>q26059159-result.txt

TYPE q26059159-result.txt

GOTO :EOF

您需要更改sourcedir 的设置以适应您的情况。

我使用包含您的数据的名为 q26059159-1.txtq26059159-2.txt 的文件进行测试。

遗憾的是,此代码有一些警告。如果提供更多信息,有些是可以避免的。

我要求提供数据样本。提供了一些人工数据。

我问分隔符是什么。我可以得出它们是分号,每个分号后面似乎都有一个空格。好像数据不是固定列的。

我问是否有空列并得到一个回避的半答案:“所需的列永远不会空”

我询问所需的列是固定的还是已知的。遗憾的是,除了可能是 要求并且可能是 a 要求的示例之外,没有任何回应 - 没有信息。

啊 - 文件的布局是相同的。

那我为什么要问?也许是为了我的健康?因为时尚?像The Elephant's Child 最爱的人一样贪得无厌的好奇心?

很简单——因为它会影响方法。

例如,for /f "tokens=...delims=..." 构造会将文本数据巧妙地分解为标记。 delims 可以指定多个字符,但通过从头开始扫描文本行并观察集合中的任何分隔符或分隔符序列来分配标记。

结果是,如果选择; 作为分隔符,每个token=field 将根据; 字符的位置进行分配。如果字段为空,则文本可能包含;;,它将被视为一个分隔符,而不是两个。这就是为什么有必要问这个问题是否有空列

在这种情况下,我们使用“;Space”分隔列。我们不能选择 both ;Space,因为数据很可能在字段中包含空格,这将被视为列分隔符,所以我们不能简单地按列计数。

结果是数据列似乎前面有一个空格。除了第一个,只是为了方便。

接下来我们看看tokens. 都非常简单。标记号 = 列号。

Except...tokens 限制为 31 个。如果您想要第 44 列,那么有一些方法和手段,但这意味着增加处理时间和更复杂的程序。

然后输出。如果要输出 column1,则应该从它剥离前导空格。额外的处理是否值得实施取决于实际情况。

根据名称自动计算列号是完全可能的。引入的并发症是否值得投资取决于问题的范围。如果它总是按照示例叙述中的描述分析第 2 列和第 4 列 - 那么可能不会。如果它将是不同的列组合,并且可能超过原始查询中的两个 - 那么,它可能可以被容纳,但这一切都以程序复杂性和执行时间为代价。

然后我问“可能有多少种独特的组合”What happened? 有一种使用变量命名的批处理技术可以使用。如果这样的组合很少,那么也许可以使用该技术。它是有限的——但它也可以很快。好吧,批量快速...

而这一切都取决于更多未说明的数据。 Batch 的字符串限制为 8,000 多一点。例如,某些对批处理具有特殊含义的字符(如果存在)需要特殊技术。

总的来说,这种方法可能根本不适用。我怀疑文件太大,一开始就无法实现。

【讨论】: