Board logo

标题: [问题求助] [已解决]PowerShell怎样删除大文件中的多余重复行? [打印本页]

作者: awk    时间: 2013-7-5 22:29     标题: [已解决]PowerShell怎样删除大文件中的多余重复行?

本帖最后由 awk 于 2013-9-5 14:14 编辑

服务器上的数据文件,大概有三千多万行的数据。
对于重复出现的行,只保留第一次出现的就行了,把多余的删掉。

样例数据:
awk PowerShell-3
awk PowerShell-3
awk PowerShell-5
awk PowerShell-3
awk PowerShell-5
awk PowerShell-1
awk PowerShell-2

处理之后得到:
awk PowerShell-3
awk PowerShell-5
awk PowerShell-1
awk PowerShell-2
作者: PowerShell    时间: 2013-7-6 10:54

本帖最后由 PowerShell 于 2013-7-6 18:15 编辑

行去重么?
powershell的默认把,数据分成行,然后存在数组中,适合小文件。也就是说性能很差。但是功能很好,用之很爽。

行去重有很多方法,我们讨论逻辑方法才是重点.我网上搜到2篇文章,你先看看
-----------------------------1--------------------------------------
C#大数据量文本数据去重方案

[日期:2013-01-15] 作者:blue1000 来源:BK网络学院 [字体:大 中 小]


   

   

C#数据去重的方法有很多种:逐一比对来判断是否重复、利用Hash.Add成功与否来判断是否重复、利用一定的排序算法排序后比对去重。逐一比对效率低下;hash.add效率高但是受内存分配限制不能处理G级数据;排序去重的方法又破坏了源数据的顺序。鉴于此,今天我们尝试使用map的思想来去重。

使用代码:

Encoding encoding = null;

static Crc32 crc = new Crc32();

//创建索引地图

CreateIndex("data.txt", "index.dat", ref encoding);

//索引去重及整理

RemoveDublicates("index.dat", "newindex.dat");

//根据去重后的索引重建数据文件,即得到去重后的数据文件

CreateFileFromIndex("data.txt", "newindex.dat", "removeddub.txt", encoding);

函数实现:

static void CreateIndex(string datapath, string indexpath, ref Encoding encoding)

{



// Encoding is very important here because of the new line string length

var source = encoding != null ? new StreamReader(datapath, encoding) : new StreamReader(datapath);

var dest = new BinaryWriter(File.OpenWrite(indexpath));

encoding = source.CurrentEncoding;



//Index is a fixed enty length binary file!! Each entry 4 bytes crx(can 4 4 bytes with crc16) 4 bytes startpos(4GB file limit) 1 bytes length (line size must be smaller than 256 bytes)

// total 9 bytes. for each line

// |--- 4 bytes CRC --- | -- 4 Bytes position -- | 1 byte len |

int start = 0;

var nllen = source.CurrentEncoding.GetByteCount(Environment.NewLine);

while (!source.EndOfStream)

{



var line = source.ReadLine();

var len = source.CurrentEncoding.GetByteCount(line);

//Remove separtor to reduce size

line = line.Replace(";", "");





dest.Write(crc.ComputeChecksum(source.CurrentEncoding.GetBytes(line)));

dest.Write((int)start);

dest.Write((byte)len);

start += len + nllen;

}

source.Close();

dest.Close();

}

static void RemoveDublicates(string indexpath, string newindexpath)

{

//Remove dublicate indexes

var source = File.OpenRead(indexpath);

//Destination is readable and writeable so can read,seek and append

var destination = File.Open(newindexpath, FileMode.Create, FileAccess.ReadWrite);



byte[] buffer = new byte[16],

sourcebuffer = new byte[16];

while (source.Position < source.Length)

{

source.Read(sourcebuffer, 0, 9);

var crc32 = BitConverter.ToInt32(sourcebuffer, 0);

destination.Position = 0;

var appeend = true;

//Look for double

while (destination.Position < destination.Length)

{

destination.Read(buffer, 0, 9);

if (BitConverter.ToInt32(buffer, 0) == crc32)

{

appeend = false;//no duplicates

break;

}

}

if (appeend)

{

destination.Seek(0, SeekOrigin.End);

// Copy

destination.Write(sourcebuffer, 0, 9);

//Write to disk

destination.Flush();

}

}

source.Close();

destination.Close();





}



static void CreateFileFromIndex(string sourcepath, string sourceindexpath, string destfilepath, Encoding encoding)

{

var sourcefile = File.OpenRead(sourcepath);

var destfile = File.Create(destfilepath);

var indexfile = File.OpenRead(sourceindexpath);

byte[] buffer = new byte[258];

byte[] indexbuffer = new byte[16];

var nllen = encoding.GetByteCount(Environment.NewLine);

while (indexfile.Position < indexfile.Length)

{

indexfile.Read(indexbuffer, 0, 9);

//Seek the line

sourcefile.Seek((long)BitConverter.ToInt32(indexbuffer, 4), SeekOrigin.Begin);

//Last byte is lenght + newlinechar length this depends on file type and encoding

var len = (int)indexbuffer[8] + nllen;

//Read line

sourcefile.Read(buffer, 0, len);

//Write Dest

destfile.Write(buffer, 0, len);

}

destfile.Flush();

destfile.Close();

sourcefile.Close();

indexfile.Close();

}

用到的hash算法类:

public class Crc32

{

uint[] table;



public uint ComputeChecksum(byte[] bytes)

{

uint crc = 0xffffffff;

for (int i = 0; i < bytes.Length; ++i)

{

byte index = (byte)(((crc) & 0xff) ^ bytes);

crc = (uint)((crc >> 8) ^ table[index]);

}

return ~crc;

}



public byte[] ComputeChecksumBytes(byte[] bytes)

{

return BitConverter.GetBytes(ComputeChecksum(bytes));

}



public Crc32()

{

uint poly = 0xedb88320;

table = new uint[256];

uint temp = 0;

for (uint i = 0; i < table.Length; ++i)

{

temp = i;

for (int j = 8; j > 0; --j)

{

if ((temp & 1) == 1)

{

temp = (uint)((temp >> 1) ^ poly);

}

else

{

temp >>= 1;

}

}

table = temp;

}

}

}
---------------------------2----------------------------------
C#实现大数据量TXT文本数据快速高效去重

[日期:2012-11-09] 作者:blue1000 来源:BK网络学院 [字体:大 中 小]


   

   

对几千万的TXT文本数据进行去重处理,查找其中重复的数据,并移除。尝试了各种方法,下属方法是目前尝试到最快的方法。以下代码将重复和不重复数据进行分文件存放,提升效率的关键是用到了HashSet。

                TextReader reader = File.OpenText(m_dataFilePath);

                string[] files = new string[2];

                files[0] = ROOT_DIR + "不重复数据.txt";

                files[1] = ROOT_DIR + "重复数据.txt";

                TextWriter writer1 = File.CreateText(files[0]);

                TextWriter writer2 = File.CreateText(files[1]);

                string currentLine;

                int idx = 0;

                HashSet<string> previousLines = new HashSet<string>(new MyEqualityComparer());

                while ((currentLine = reader.ReadLine()) != null)

                {

                    if ((++idx % 10000) == 0)

                        UpdateInfo("正在比对第 " + idx + " 条数据…");

                    currentLine = currentLine.TrimEnd();

                    if (previousLines.Add(currentLine))

                    {

                        writer1.WriteLine(currentLine);

                    }

                    else

                    {

                        if(m_allSave)

                            writer2.WriteLine(currentLine);

                    }

                }

                reader.Close();

                writer1.Close();

                writer2.Close();

                reader.Dispose();

                writer1.Dispose();

                writer2.Dispose();

1000万数据的处理时间也就是转瞬之间,试试看?
------------------------------------------------------------------
声明,这是转载, 来自 http://www.blue1000.com/bkhtml/2012-11/70985.htm
--------------------------------------------------------------

第二个很简单吧,只要把这个代码翻译成ps代码即可。这个翻译看看抽空我能做不

下一步就是测试了,你都熟悉啥语言,啥工具呢?
你能帮忙测试么?
或者把你的文本压缩包,传上来。让某个帖子看官测试。
作者: ShadowFiend    时间: 2013-7-6 16:20

回复 2# PowerShell


    楼主想用PS代码实现啊,把代码写下来~
作者: Demon    时间: 2013-7-6 17:57

行去重么?
powershell的默认把,数据分成行,然后存在数组中,适合小文件。也就是说性能很差。但是功能很 ...
PowerShell 发表于 2013-7-6 10:54


传教士大人你昨天不是说无敌的PowerShell不需要算法的么,不是应该调用某个类就能轻松解决的么?
作者: PowerShell    时间: 2013-7-6 18:14

算法,应该是冒泡,二叉遍历之类的.算法,要涉及到底层的数据结构,
所以应该是方法,更确切些,方法是指调用哪些类库,已经更正.
吹毛球p的家伙啊,我要用一个p把你蹦飞~~~
作者: CrLf    时间: 2013-7-6 18:54

gnu 的第三方还是很爽的:
  1. sort a.txt|uniq
复制代码
  1. gawk "!a[$0]++" a.txt
复制代码
sed用在这里效率应该比较低,就不举例了。
另,
hash.add效率高但是受内存分配限制不能处理G级数据

无语,ms 也太懒了,我所了解的三种 hash 算法都无此限制,因为它们都是逐字节或逐段计算的。不了解  c#,不作过多评论。
作者: Demon    时间: 2013-7-7 18:34

gnu 的第三方还是很爽的:sed用在这里效率应该比较低,就不举例了。
另,

无语,ms 也太懒了,我所了解 ...
CrLf 发表于 2013-7-6 18:54



你把哈希表跟哈希算法搞混了吧。
作者: awk    时间: 2013-7-7 18:39

回复 2# PowerShell


看不懂C#,也不想学,因为我的工作中根本用不到。
文件太大了,十几个GB,不好传啊。
版主能否先给个针对小文件的PowerShell去重代码?我先学习一下。
作者: awk    时间: 2013-7-7 18:42

回复 5# PowerShell


我记得冒泡,二叉遍历之类的好像是用来排序的吧,以前学校里面学过,现在都忘了。
如果说错了请大家指正。
去重之前必须要排序吗?好复杂的说。
作者: CrLf    时间: 2013-7-7 21:42

回复 7# Demon


    不甚明了,求指点
作者: Demon    时间: 2013-7-7 22:21

回复  Demon


    不甚明了,求指点
CrLf 发表于 2013-7-7 21:42


http://en.wikipedia.org/wiki/Hash_function

http://en.wikipedia.org/wiki/List_of_hash_functions

http://en.wikipedia.org/wiki/Hash_table

一个是算法,一个是基于此类算法的数据结构。
作者: PowerShell    时间: 2013-7-7 22:28

谁最好给个测试用的数据,的压缩包的,下载地址啊。最好是长期的。
作者: batman    时间: 2013-7-8 09:42

这传的是哪门子教?口水教。。。
作者: awk    时间: 2013-7-8 19:59

回复 12# PowerShell


    我在顶楼放了点数据
作者: awk    时间: 2013-7-12 19:44

回复 12# PowerShell


    版主最近忙什么呢?数据放在顶楼好几天了,求代码啊。
作者: Batcher    时间: 2013-7-30 13:58

  1. get-content a.txt | sort -unique
复制代码
  1. get-content a.txt | sort | get-unique
复制代码

作者: luke    时间: 2013-9-4 00:08

$hashset=New-Object System.Collections.Generic.HashSet[int]
$duplicateFile="d:\1\duplicate.log"
$uniqueFile="d:\1\unique.log"
$sourceFile="D:\1\u_ex13061100.log"
Get-Content $sourceFile | %{
    if($hashset.add($_.GetHashCode())){
        $_ | Out-File -FilePath $uniqueFile -Append
    }else {
        $_ | Out-File  -FilePath $duplicateFile -Append
    }
}
作者: awk    时间: 2013-9-5 14:14

回复 17# luke


    正解。感谢!
作者: PowerShell    时间: 2013-9-5 21:34

17楼的代码显然不行,实验后更不行。
1 貌似不应该用Get-Content。我想,或许,应该用streamreader。
2 貌似不应该用out-file -append。我想,或许,应该用stringbuilder
3 绝对不应该用string.gethashcode()  。具体原因参见 http://msdn.microsoft.com/zh-cn/ ... ng.gethashcode.aspx

说明
如果两个字符串对象相等,则 GetHashCode 方法返回相同的值。 但是,每个唯一的字符串值并没有唯一的哈希代码值。 不同的字符串可以返回相同的哈希代码。
有关哈希代码的更多信息,请参见 Object.GetHashCode。
对调用者的说明
GetHashCode  返回的值与平台相关。 在 32 位和 64 位版本的 .NET Framework 上有所不同。


这个问题比较难,或者说我水平太差。等我有空研究下。或者坐等牛人。

顾请楼主把标题改成未解决。
作者: luke    时间: 2013-9-9 11:15

版主说的不错,不同的字符串可能会产生相同的哈希值,这个要得看需求了,可以改用md5算法。
作者: terse    时间: 2013-9-9 14:39

  1. $AFile = "c:\test\a.txt"
  2. $DFile = "c:\test\add.txt"
  3. $Hash = @{}
  4. Get-Content $AFile | %{
  5. if(!$Hash.ContainsKey($_)){
  6. $_
  7. $Hash.Add($_ , 1)
  8. }
  9. } | Out-File $DFile
  10. $Hash.Keys
  11. CMD /C "PAUSE"
复制代码

作者: softy    时间: 2013-10-6 17:25

$spath="f:\Code\powershell\重复行.txt"
$dpath="f:\Code\powershell\去重复行.txt"

[System.Collections.Generic.HashSet[string]]$lines=Get-Content $spath -ReadCount 0
Set-Content  -Path $dpath -Value ($lines -join "`r`n")

以前写的,代码很简单,速度也不错,文件大时比较耗内存而已,可能需要3.0以上版本,2.0版的估计要修改下代码




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2