Chinaunix

标题: 请教一个替换文本文件中指定位置的值的perl的写法! [打印本页]

作者: robinhappiness    时间: 2015-10-22 13:21
标题: 请教一个替换文本文件中指定位置的值的perl的写法!
请教一个perl的写法!谢谢!
将input.txt文件中的第2byte的值设定到第11byte,输出output.txt

处理对象文件内容
$cat input.txt
1F 123456 T qqqqq zzz              
2G 7测012 T qqqqq zzz
3V 1试456 T qqqqq zzz
4H 234567 T qqqqq zzz
90000000

想要的结果是
$cat output.txt
1F 123456 F qqqqq zzz            
2G 7测012 G qqqqq zzz
3V 1试456 V qqqqq zzz
4H 234567 H qqqqq zzz
90000000
作者: sunzhiguolu    时间: 2015-10-22 15:20
本帖最后由 sunzhiguolu 于 2015-10-22 15:26 编辑

有几点不太明白, 还请楼主解释一下:
1.> 主机环境是什么? (Linux | Windows)
2.> 您的文本中汉字占用几个字节? (1 | 2 | 3 | 4)
作者: robinhappiness    时间: 2015-10-22 15:30
sunzhiguolu 发表于 2015-10-22 15:20
有几点不太明白, 还请楼主解释一下:
1.> 主机环境是什么? (Linux | Windows)
2.> 您的文本中汉字占用几个 ...


你好!
1.主机环境是Linux
2.文本中汉字占用两个字节
作者: 815138698    时间: 2015-10-22 15:32



这样就可以搞定吧~~~

s/(.)(.)(........)(.)(.*)/$1$2$3$2$5/;
作者: sunzhiguolu    时间: 2015-10-22 15:38
本帖最后由 sunzhiguolu 于 2015-10-22 15:57 编辑

回复 3# robinhappiness
确定汉字在文本中占用两个字节? 您测试过了吗? (编码方式是什么)
能否将测试的结果贴出来?

   
作者: robinhappiness    时间: 2015-10-22 16:26
sunzhiguolu 发表于 2015-10-22 15:38
回复 3# robinhappiness
确定汉字在文本中占用两个字节? 您测试过了吗? (编码方式是什么)
能否将测试的结 ...


测试了一下您的sed命令,请教一个问题。

处理对象文件内容
$cat target.txt
1F 123456 T qqqqq zzz              
2G 7测012 T qqqqq zzz
3V 1试456 T qqqqq zzz
4H 234567 T qqqqq zzz
90000000

实际执行结果
1F 123456 F qqqqq zzz              
2G 7测012 TG qqqqq zzz
3V 1试456 TV qqqqq zzz
4H 234567 H qqqqq zzz
90000000


想要的结果是
1F 123456 F qqqqq zzz            
2G 7测012 G qqqqq zzz
3V 1试456 V qqqqq zzz
4H 234567 H qqqqq zzz


----------------
我是这样实现的。
cut原文件的第2列,aaa.txt
然后再cut原文件的1到10列,bbb.txt
然后再cut原文件的12列以后,ccc.txt

然后取消aaa.txt bbb.txt 换行之后合并cat bbb.txt aaa.txt ccc.txt > output.txt

感觉这个办法有点儿饶,所以想请教有没有更好的办法。
作者: sunzhiguolu    时间: 2015-10-22 17:21
处理汉字的问题我也比较菜鸟, 恐怕让您失望了.
只有请高手替您解答这个问题了, 我也想知道如何完美解决这个问题的方法. 帮顶!
作者: zhlong8    时间: 2015-10-22 22:12
应为有 90000000 这种不需要替换的行,所以单纯的 substr 不行。

use bytes;  #他能保证匹配是按字节来的,无需考虑中文字符的问题。这里没必要加就是个保险
while (<>) {
    s/^(.(.) .{6} )./$1$2/; # 有两个空格,用来区分格式,避开 9000000 这样的行
    print $_;
}
作者: MMMIX    时间: 2015-10-22 22:37
回复 1# robinhappiness


    直接按域处理么,数什么字节。

#!/usr/bin/perl

use strict;
use warnings;

use v5.14;

while (<DATA>) {
  chomp;
  my @f = split;
  say and next unless @f > 1;

  $f[2] = (split //, $f[0])[1];
  say join " ", @f;
}

__DATA__
1F 123456 T qqqqq zzz
2G 7测012 T qqqqq zzz
3V 1试456 T qqqqq zzz
4H 234567 T qqqqq zzz
90000000

作者: sunzhiguolu    时间: 2015-10-23 09:02
本帖最后由 sunzhiguolu 于 2015-10-23 09:22 编辑

回复 8# zhlong8
大神您好, 在 Linux 环境下如何确保中文字符按照 个字节进行处理.
测试环境:linux
测试文本:
1.> 编码方式: utf-8
2.> 使用桌面环境, 右键创建空文档的方式创建的测试文件 input.txt

本地语言环境:
  1. LANG=zh_CN.UTF-8
  2. LC_CTYPE="zh_CN.UTF-8"
  3. LC_NUMERIC="zh_CN.UTF-8"
  4. LC_TIME="zh_CN.UTF-8"
  5. LC_COLLATE="zh_CN.UTF-8"
  6. LC_MONETARY="zh_CN.UTF-8"
  7. LC_MESSAGES="zh_CN.UTF-8"
  8. LC_PAPER="zh_CN.UTF-8"
  9. LC_NAME="zh_CN.UTF-8"
  10. LC_ADDRESS="zh_CN.UTF-8"
  11. LC_TELEPHONE="zh_CN.UTF-8"
  12. LC_MEASUREMENT="zh_CN.UTF-8"
  13. LC_IDENTIFICATION="zh_CN.UTF-8"
  14. LC_ALL=
复制代码
测试代码如下: (只抓取前 10 个字符)

  1. #!/usr/bin/perl -w
  2. use strict;
  3. use bytes;

  4. while (<>){
  5.    if (m{(.{10})}){
  6.        my $leng = length ($1);
  7.        printf "Length = %d, |%s|\n", $leng, $1;
  8.        my @a_10c = split //, $1;
  9.        foreach (@a_10c){
  10.           printf "<%s>=%d ", $_, length ($_);
  11.        }
  12.        print "\n";
  13.     }
  14. }
复制代码
输出结果如下:

  1. Length = 10, |1F 123456 |
  2. <1>=1 <F>=1 < >=1 <1>=1 <2>=1 <3>=1 <4>=1 <5>=1 <6>=1 < >=1
  3. Length = 10, |2G 7测012|
  4. <2>=1 <G>=1 < >=1 <7>=1 <?>=1 <?>=1 <?>=1 <0>=1 <1>=1 <2>=1
  5. Length = 10, |3V 1试456|
  6. <3>=1 <V>=1 < >=1 <1>=1 <?>=1 <?>=1 <?>=1 <4>=1 <5>=1 <6>=1
  7. Length = 10, |4H 234567 |
  8. <4>=1 <H>=1 < >=1 <2>=1 <3>=1 <4>=1 <5>=1 <6>=1 <7>=1 < >=1
复制代码
输出结果中连续的 3 个 ? (问号字符) 表示一个汉字字符, 每个汉字部分占用 3 个字节长度.

   
作者: zhlong8    时间: 2015-10-23 09:13
回复 10# sunzhiguolu


    你最好按9楼写的那样换种思路,utf8 中文常用字都是3个字节的,但是后来陆续添加了不少生僻字长度就没法保证了。你还要把他们转换成两字节的,明显误入歧途了
作者: sunzhiguolu    时间: 2015-10-23 09:18
回复 11# zhlong8
谢谢您的提醒, 在 Windows 环境的确是按照每个汉字占用 2 字节进行处理的. 非常感谢您的帮助, 谢谢...

   
作者: robinhappiness    时间: 2015-10-23 09:29
非常感谢各位高人的回复!

我之前做的测试数据比较规范,实际上要处理的对象文件里面包含有全角和半角的空格、汉字、数字、符号等都有,
而且每个字段的划分也没规律,打个比方我要把第2byte的数值插入到第25byte的位置(zz0和yyy之间),
第1和第2byte位置的值肯定只占一个字节,还有第25byte的位置肯定为只站一个字节的半角空格。
字段的布局没有规律!

想要做的事情就是把文件中的第2byte位置的列值(只占1个字节的字母)
赋值到文件中第25byte空格(只占一个字节的半角空格)的位置。

对象文件
$cat input.txt
1000000
1F 123456 ー  qqq q zz0 yyy  
2G 7测012 --  q qq  zz0 yyy
3V 1试456     qq  qq zz0 yyy
4H 234567    qqq  q zz0 yyy
09000000

想要的输出结果
$cat output.txt
1000000
1F 123456 ー  qqq q zz0Fyyy  
2G 7测012 --  q qq  zz0Gyyy
3V 1试456     qq  qq zz0Vyyy
4H 234567    qqq  q zz0Hyyy
09000000
作者: sunzhiguolu    时间: 2015-10-23 09:38
本帖最后由 sunzhiguolu 于 2015-10-23 09:39 编辑

回复 9# MMMIX
#!/usr/bin/perl
use strict;
use warnings;
use v5.14;

while (<DATA>) {
  chomp;
  my @f = split;
  say and next unless @f > 1;

  $f[2] = (split //, $f[0])[1];
  say join " ", @f;
}

__DATA__
1F 123456 T qqqqq zzz
2G 7测012 T qqqqq zzz
3V 1试456 T qqqqq zzz
4H 234567 T qqqqq zzz
90000000

请问大神, and 在 say 函数和 next unless @f > 1 语句块之间起到一个什么作用? (不知道划分的是否正确?)


   
作者: MMMIX    时间: 2015-10-23 20:35
回复 14# sunzhiguolu


    say and next unless @f > 1;

等价于

   if (@f > 1) {
       say;
       next;
   }

第二种写法更好。
作者: MMMIX    时间: 2015-10-23 20:49
回复 13# robinhappiness


    这个就更简单了,用 split + join 就可以了:

while (<DATA>) {
  my @f = split //;
  $f[24] = $f[1] if @f > 24;
  print join '', @f;
}

作者: sunzhiguolu    时间: 2015-10-23 20:51
本帖最后由 sunzhiguolu 于 2015-10-23 20:54 编辑

回复 15# MMMIX
say and next unless @f > 1;

大神, 您这句不是针对 90000...行 写的判断条件吗?
如果上面的这个语句与您下面的这句等价, 那么最后一行不就多余了吗?
if (@f > 1){
   say;
   next;
}
   
作者: MMMIX    时间: 2015-10-23 21:02
回复 17# sunzhiguolu


    把我改写的替换进去,然后再按你的理解把觉得多余的语句删除看看效果。
作者: robinhappiness    时间: 2015-10-25 21:53
MMMIX 发表于 2015-10-23 20:49
回复 13# robinhappiness


我上传一个input文件和一个output文件图片来说明一下。
文件内容中,每条记录的长度都占50byte,在每行的第51byte位置开始换行。
除了第一行和最后一行之外的第2byte位置的值需要设定到第25byte位置。
处理前后的文件大小不能够有差异。文件中含有半角,全角的字符,数字,空格等。

input.txt:

1sadw2we21                                       
1F 123456  ー-  qqq  00      00000000            
2G 7漢012 -  -  qq  00      00000000            
3V 1字456   /  q q  00      00000000            
4H 234567 1 2 3  q 00      00000000            
0900000000                                       


output.txt:

1sadw2we21                                       
1F 123456  ー-  qqq  00F     00000000            
2G 7漢012 -  -  qq  00G     00000000            
3V 1字456   /  q q  00V     00000000            
4H 234567 1 2 3  q 00H     00000000            
0900000000                                       

作者: xie3ks    时间: 2015-10-26 17:32
本帖最后由 xie3ks 于 2015-10-26 17:35 编辑
  1. #!/usr/bin/perl
  2. use warnings;
  3. use strict;

  4. while(<DATA>)
  5. {
  6.     my @bytes_value=split //;

  7.     $bytes_value[10]=$bytes_value[1] if defined $bytes_value[10];
  8.     print join "",@bytes_value;
  9. }

  10. __DATA__
  11. 1F 123456  T qqqqq zzz              
  12. 2G 7测012 T qqqqq zzz
  13. 3V 1试456 T qqqqq zzz
  14. 4H 234567 T qqqqq zzz
  15. 90000000
复制代码





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2