【问题标题】:SED or AWK script to replace multiple text用于替换多个文本的 SED 或 AWK 脚本
【发布时间】:2018-07-02 16:18:29
【问题描述】:

我正在尝试使用 sed 脚本执行以下操作,但这需要太多时间。看起来我做错了什么。

场景: 我在students.txt 中有学生记录(> 100 万)。 在这个文件中(每行)前 10 个字符是学生证,接下来的 10 个字符是联系电话等等

students.txt

10000000019234567890XXX...
10000000029325788532YYY...
.
.
.
10010000008766443367ZZZZ...

我有另一个文件 (encrypted_contact_numbers.txt),其中包含除号码以外的所有电话以及相应的加密电话号码,如下所示

encrypted_contact_numbers.txt

Phone_Number、Encrypted_Phone_Number

9234567890, 1122334455
9325788532, 4466742178
.
.
.
8766443367, 2964267747

我想用encrypted_contact_numbers.txt中对应的加密电话号码替换students.txt中的所有联系号码(第11-20位)。

预期输出:

10000000011122334455XXX...
10000000024466742178YYY...
.
.
.
10010000002964267747ZZZZ...

我正在使用下面的 sed 脚本来执行此操作。它运行良好,但速度太慢。

方法一:

while read -r pattern replacement; do   
    sed -i "s/$pattern/$replacement/" students.txt
done < encrypted_contact_numbers.txt

方法2:

sed 's| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|' <encrypted_contact_numbers.txt |
sed -f- students.txt > outfile.txt

有什么方法可以快速处理这个巨大的文件?

更新:2018 年 2 月 9 日

如果电话号码在指定位置(第 10-20 列),AWK 和 Perl 中给出的解决方案工作正常,如果我尝试进行全局替换,则需要花费太多时间来处理。有没有最好的方法来实现这一点?

students.txt:更新版本

10000000019234567890XXX...9234567890
10000000029325788532YYY...
.
.
.
10010000008766443367ZZZZ9234567890...

【问题讨论】:

  • 编辑问题以包含 sed 脚本。
  • 除了约翰提到的,请在代码标签中添加您的 encrypted_contact_numbers.txt。
  • 为了正确的格式,请在每行代码/数据/错误消息的前面使用4个空格或突出显示一个文本块并使用编辑左上角的{}格式工具框格式为code/data/output/errorMsgs。有关详细信息,请参阅 editing-help 和 formatting。祝你好运。

标签: python perl unix awk sed


【解决方案1】:

awk 来救援!

如果您有足够的内存将 phone_map 文件保存在内存中

awk -F', *' 'NR==FNR{a[$1]=$2; next}
                    {key=substr($0,11,20)}
           key in a {$0=substr($0,1,10) a[key] substr($0,21)}1' phone_map data_file

未测试,因为您缺少数据文件。它应该会加快速度,因为这两个文件只会被扫描一次。

【讨论】:

  • 我已经添加了示例数据文件(Students.txt)
  • @Dhanabalan,请尝试将所有详细信息一次性附加到您的帖子中,尝试使用 Karakfa 和我的代码,然后让我们知道结果如何。
  • 我错过了什么吗?第一个动作块中的next 不会在每一行上触发吗?如果是这样,其他两个块将永远不会运行。应该是NF&gt;1 {a[$1]=$2]; next}?另外,我认为第三个操作块之后的1 放错了位置。
  • 你当然是对的。 “第一个文件”条件丢失,现在添加。我直接在这里输入,没有先测试。
  • NR==FNR 是一个不错的技巧,我以前从未见过 - 谢谢! (对于未来的读者,当且仅当您所在的行来自命令行上的第一个文件时才是正确的。)
【解决方案2】:

关注awk 可能对您有所帮助。

awk '
FNR==NR{
  sub(/ +$/,"");
  a[$1]=$2;
  next
}
(substr($0,11,10) in a){
  print substr($0,1,10) a[substr($0,11,10)] substr($0,21)
}
' FS=", " encrypted_contact_number.txt students.txt

输出如下。很快就会添加解释。

10000000011122334455XXX...
10000000024466742178YYY...

【讨论】:

  • 这适用于示例。冷,请您解释一下代码,以便将其实现到我的实际场景中,并带有巨大的文件。再次感谢您的帮助!
  • 我能够理解并尝试在实际场景中实现相同的功能。它工作正常,感谢您的及时帮助!
  • 嗨拉文德。是否可以在不提及位置的情况下将所有匹配号码替换为加密号码?喜欢全部替换
  • @Dhanabalan,请举例说明,不清楚。如果这是一个新问题,那么也将它发布到一个新线程中。
  • 最初我告诉第一个 10 个字符是学生证,接下来的 10 个字符是联系电话。所以我们使用 substr($0,11,10) 进行替换。现在我得到了在其他位置也有联系号码的文件,所以我尝试全局替换所有而不是特定位置。
【解决方案3】:

如果没有 Perl 答案,什么问题会是完整的? :) 改编自本主题Perl Monks' discussion 中的各种答案。

编辑源

根据@Borodin 的评论编辑。附上一些内联的cmets进行说明,希望对大家有所帮助。

#!/usr/bin/env perl

use strict;     # keep out of trouble
use warnings;   # ditto

my %numbers;    # map from real phone number to encrypted phone number

open(my $enc, '<', 'encrypted_contact_numbers.txt') or die("Can't open map file");
while(<$enc>) {
    s{\s+}{}g;                               #remove all whitespace
    my ($regular, $encrypted) = split ',';
    $numbers{$regular} = $encrypted;
}

# Make a regex that will match any of the numbers of interest
my $number_pattern = join '|', map quotemeta, keys %numbers;
$number_pattern = qr{$number_pattern}o;
    # Compile the regex - we no longer need the string representation

while(<>) {     # process each line of the input
    next unless length > 1;     # Skip empty lines (don't need this line if there aren't any in your input file)
    substr($_, 10, 10) =~ s{($number_pattern)}{$numbers{$1}}e;
    # substr: replace only in columns 11--20
    # Replacement (s{}{}e): the 'e' means the replacement text is perl code.
    print;  # output the modified line
}

测试

在 Perl v5.22.4 上测试。

encrypted_contact_numbers.txt:

9234567890, 1122334455
9325788532, 4466742178

students.txt:

aaaaaaaaaa9234567890XXX...
bbbbbbbbbb9325788532YYY...
cccccccccc8766443367ZZZZ...
dddddddddd5432112345Nonexistent phone number

(为便于阅读而修改)

输出来自./process.pl students.txt:

aaaaaaaaaa1122334455XXX...
bbbbbbbbbb4466742178YYY...
cccccccccc8766443367ZZZZ...
dddddddddd5432112345Nonexistent phone number

对前两行进行了更改,但不是后两行,这对于此输入是正确的。

【讨论】:

  • 当您发布没有use strict 或use warnings 的答案,使用两个参数open,这会削弱我们鼓励最佳做法的努力,在die 字符串和全局文件句柄。另外——你为什么在数字字符串上使用quotemeta? -- 你的$numbers{$1} || $1 毫无意义——你只是用散列键构建了正则表达式模式。在您完成使用它构建正则表达式之后,您还保留了超过一百万条记录的哈希值。
  • @Borodin 因为我还在学习,并在这样做的同时尝试回馈。感谢您的反馈! quotemeta 因为我不想假设 OP 的文件只有数字。我同意应该是这样,但安全总比抱歉好。 || $1 如果我忽略它,我会在匹配的行上得到Use of uninitialized value within %numbers in substitution iterator。为什么?
  • @Borodin 我无法再重现我遇到的问题 - 不确定交易是什么。无论如何,固定版本在上面。我确实需要在主循环中保留替换值的哈希值,但不需要保留 join 结果,所以我改变了它。
  • “没有 cmets 会更短!” 你应该把它们排除在外。在你编写的每个程序中教人们 Perl 不是你的地方。 “如果我忽略这个,我会得到 Use of uninitialized value within %numbers in substitution iterator” 我无法得到这种行为。 // 此外,您不应该在替换时使用 /g 修饰符,并且您没有理由避免 s/// 和 m// 的标准斜杠分隔符。
  • @Borodin 是的,我去掉了 /g 修饰符。 s{}{} 只是因为在我的浏览器中,语法荧光笔无法正确解析 s///。回覆。 cmets,OP专门询问了另一个回答者“请您解释一下代码”。不管这是否是我的地方,我认为在期待类似请求的情况下发表评论并不是没有道理的。
猜你喜欢
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 2011-02-22
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多