我有两个非常大的.txt文件(约500k行)。我需要从两个文件中取两列(按列名称)并将它们相互比较(类似于LEFT JOIN在SQL中的工作方式)。所以我需要输出第三个txt / csv文件中第一个文件中两列中第二个文件中不存在的值的所有组合。
我需要自动化这个过程,所以我应该可以从命令行调用它。如果有人能指出我正确的方向,我会非常感激。
更新 文件的格式完全相同,所需的列永远不会为空。
示例
第一个文件
数据源;顾客;市;映射; SugGroup
艺术;约翰;伦敦;佐尼; LondonCustomers
艺术;克里斯;慕尼黑; JONS;德国
远百;玛丽;伦敦;詹姆士;德国
第二档
数据源;顾客;市;映射; SugGroup
艺术;克里斯;慕尼黑; JONS;德国
远百;玛丽;伦敦;詹姆士;德国
我需要做的是选择两列:Customer和Mappings。并查找第一个文件中的行而不是第二个文件中的行。因此,在给定的示例中,输出文件将如下所示:
输出文件:
客户;映射
约翰;约翰尼拉
答案 0 :(得分:1)
我建议反对Import-CSV
,因为它对100+ Mb范围内的文件效果不佳。嗯,它有效,但是狗很慢。
创建哈希表。逐行读取第二个文件。 Catenate两列并将结果存储在哈希表中。逐行读取第一个文件并连接其两列以获得类似的密钥。检查哈希表是否包含相同的密钥。如果没有,请将数据保存到第三个文件。
有关代码示例,请提供示例输入和所需输出。
您没有指定是否可以拥有相同的客户,映射但是可以更改其他数据。假设不是这种情况,只需计算整行的哈希值,
# 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
找到。它留给读者练习如何提取相关元素。
答案 1 :(得分:1)
@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.txt
和q26059159-2.txt
文件进行测试。
可悲的是,这段代码有一些警告。通过提供更多信息可以避免一些。
我要求提供数据样本。提供了一些人工数据。
我问分隔符是什么。我可以得出它们是分号,每个分号后面跟着一个空格。似乎数据不是固定列。
我问是否有空栏并且收到了一个回避的半答案:“所需的列永远不会是空的”
我询问所需的列是固定的还是已知的。遗憾的是,除了可能 要求并且可能 要求的示例之外没有任何回复 - 没有信息。
啊 - 文件的布局是一样的。
那我为什么要问?为了我的健康也许吧?因为这是时尚吗?像The Elephant's Child这样无法满足的好奇心最受欢迎?
简单 - 因为它会影响方法。
例如,for /f "tokens=...delims=..."
构造将整齐地将文本数据分解为标记。 delims
可以指定为多个字符,但是通过从头开始扫描文本行并从集合中观察任何分隔符或分隔符序列来分配标记。
结果是,如果选择;
作为分隔符,则将根据;
个字符的位置分配每个token =字段。如果字段为空,则文本可能包含;;
,这将被视为一个分隔符,而不是两个。这就是为什么有必要问问题是否有空列。
在这种情况下,我们将“; Space ”分隔列。我们无法选择 ;
和 Space ,因为数据很可能在字段中包含空格并且会被视为列分隔符所以我们不能简单地计算列数。
结果是数据列似乎有一个前置空格。除了第一个之外的所有,只是为了方便。
接下来我们看看tokens.
所有非常简单。 token-number = column-number。
除了...令牌限制为31.如果你想要第44列,那么有方法和手段,但这意味着增加处理时间和更复杂的程序。
输出。如果要输出column1,则应从 中去除前导空格。是否值得实施的额外处理取决于实际情况。
很有可能从名称中自动计算列号。引入的复杂性是否值得投资取决于问题的范围。如果它总是按照示例旁白中所描述的那样分析第2列和第4列 - 那么可能没有。如果它将是不同的列组合,并且可能超过原始查询中的两个 - 那么,它可能可以适应,但所有这都以程序复杂性和执行时间为代价。
然后我问“有多少个独特的组合”What happened?有一种批量技术可以使用变量命名。如果这种组合很少,那么也许可以使用这种技术。这是有限的 - 但它也可以很快。好吧,快批量......
所有这些都取决于更多未说明的数据。批处理的字符串限制超过8,000个字符。批量具有特殊含义的某些字符(如果存在)需要特殊技术,例如。
总的来说,这种方法可能根本不适用。我怀疑这些文件太大了,不能让这个文件可行。