【问题标题】:Write in a file if picture found in a folder如果在文件夹中找到图片,则写入文件
【发布时间】:2013-10-29 00:03:42
【问题描述】:

我有一个 csv 文件,其中 第一列是由数字和大写字母组成的产品代码列表第二列中有图片名称的可用空间 strong> 第一列的乘积。

我还有一个包含几乎所有图片的文件夹,但图片的代码是产品代码的子字符串(编辑:前缀)。图片和产品之间的匹配是一对多的,所以一个产品共享同一个图片。 例如:

3234P3001 and 3234P3002 have the same picture 3234P30

我需要制作一个 扫描 csv 文件中的代码列表的 shell 脚本,如果有一张图片的名称是该代码的子字符串,它会在第二列中写入该图片的名称强>.

这是我的第一个大项目,我没有数据处理经验。

找到图片之间的最大子串是我的产品图片。

【问题讨论】:

  • 你试过什么...
  • 不知道怎么写
  • 由于许多图片可以有一个代码,所以应该在第二列中列出多张图片,还是多行(代码,图片)或者只是找到第一张就足够了?
  • @n0741337 对于一种产品,最多只有一张照片。找到图片之间的最大子串是我的产品的图片。我的问题是对读写文件的命令缺乏了解。
  • @eliosolutions - 明白了。一开始并没有注意到这种关系——产品代码是图片名称的前缀。

标签: regex bash shell csv


【解决方案1】:

这几乎可以满足您的需求。

假设您的产品代码存储在一个名为 products.csv 的文件中,如果您将下面的代码保存在一个名为“go”的文件中,那么执行

chmod +x go
./go < products.csv

它可能需要一点点调整......

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use Cwd;

my $Debug=1;        # Set to 0 to turn off debug output
my $photosdir="/tmp";   # Or wherever your photos are

# Go to photos directory and load names of all JPEGs into array @photos
chdir $photosdir or die "Unable to chdir() to $photosdir\n";
my @photos=<*.jpg>;

# Debug - output photo filenames
print Dumper @photos if $Debug;

# Read product codes from our stdin
while(<>){
   chomp;
   my $product = $_ ;
   $product =~ s/;.*//;

   print "Finding photo for product: $product\n" if $Debug;

   # Run through all photo filenames and find longest match
   my $longestmatch=0;
   my $bestimage="<NONE>";

   foreach my $photo (@photos){
         # Strip extension off photo name
         $photo =~ s/\.jpg//;

         print "Assessing photo $photo\n" if $Debug;

         if($product =~ m/(^$photo)/ ){
            my $matchlength = length($&);
            if($matchlength > $longestmatch){
               print "Best match so far: $photo, ($matchlength characters)\n" if $Debug;
               $longestmatch = $matchlength;
               $bestimage = $photo . ".jpg";
            }
         }
   }
   print "$product,$bestimage\n";
}

实际上,您可以使用散列更优雅、更快地做到这一点。与其查看数千张照片中的每一张,直到找到最长的匹配项,不如尝试查看产品的前 n 个字母是否在哈希中,如果不是,则尝试前 n-1 个字母,然后尝试前 n-2 个字母, 像这样。对于大量产品和照片,它应该运行得更快。

#!/usr/bin/perl

use strict;
use warnings;
use Data::Dumper;
use Cwd;

my $Debug=1;        # Set to 0 to turn off debug output
my $photosdir="/tmp";   # Or wherever your photos are

# Go to photos directory and load names of all JPEGs into array @filenames
chdir $photosdir or die "Unable to chdir() to $photosdir\n";
my @filenames=<*.jpg>;

# Now create hash of photonames without ".jpg" extension
my %photos;
for my $photo (@filenames){
   $photo =~ s/\.jpg//;
   # So if there was a file "xyz.jpg", $photos{"xyz"} will be defined
   $photos{$photo}=1;
}

# Debug - output photo filenames
print Dumper \%photos if $Debug;

# Read product codes from our stdin
while(<>){
   chomp;   # remove end of line
   my ($product,$field2,$field3) = split ";";

   print "Finding photo for product: $product\n" if $Debug;

   my $bestimage="<NONE>";  # Preset and overwrite if better one found

   # Keep removing last character of product till it matches a photo
   for(my $i=length($product);$i;$i--){
      my $short = substr($product,0,$i);
      print "Trying $short\n" if $Debug;
      if(defined($photos{$short})){
         $bestimage = $short . ".jpg";
         last;
      }
   }
   print "$product;$bestimage;$field3\n";
}

【讨论】:

  • 嗨,谢谢你的解决方案,我收到一个错误 Unable to chdir() to... 是因为我使用的是 Mac OS X 吗?
  • 好的,我用“tmp”代替了“/tmp”
  • 我的 csv 是这样的:3234P3001;;aa 但是找到匹配项时脚本没有写入 csv
  • 你好。在这里你可以找到 Dropbox 上的数据,它还在上传中……dropbox.com/sh/xq2q0k4bwa25s6u/3C16EtdE2u
  • @eliosolutions 问题可能是您的 .csv 有奇怪的(损坏的?)行尾(仅限 CR,既不是 UNIX 也不是 DOS 样式)。我所知道的工具都没有处理这个问题。如果您先修复它,脚本就会正常工作,并将图像添加到示例数据中 6468 行中的 326 行。见这里:paste.ubuntu.com/6330522
【解决方案2】:

您可以即时组装 sed 脚本,替换每个存在的图像文件

#!/bin/bash

sed -i -f <(
    find images/ -type f -name '*.jpg' | LANG=C sort -r | 
    while read imagename
    do
        basename=$(basename "$imagename" .jpg)
        echo "s#^\\($(printf "%q" "$basename")[^;]*;\\);#\\1$imagename;#"
    done) "$@"

注意事项:

  • 脚本是动态编译的 sed 脚本
  • 我对文件名进行降序排序(因此具有公共前缀的最长图像名称将被优先处理
  • 我只扫描图像目录一次(为了提高性能,也为了能够创建可预测的结果)
  • 更新:现在让脚本实际处理带有公共前缀的图像(例如3234.png3234P30.png)。最长的匹配将占上风 - 因为sort -r 步骤)

示例:对于输入文件

3234P3001;;aa
3234P3002;;bb

执行script.sh input 将导致

3234P3001;/tmp/images/3234P30.png;aa
3234P3002;/tmp/images/3234P30.png;bb

【讨论】:

  • 感谢您的意见,我会尽快尝试并通知您。代码是7k,图片是1k,所以我不知道是扫描产品并在图片中找到子字符串还是扫描图片并找到具有相同前缀的产品更好。你怎么看?
  • 我按照自己的方式提出了解决方案 :) 现在,您将只对图像文件进行一次处理,对输入文件进行一次处理。这是 O(n+m) 复杂度。
  • 扫描产品 O(n),然后在图片中找到一个子字符串将是 O(m)(实际上更糟,因为你一开始不知道要查找什么长度前缀.. .O(mavg_suffixlength)),这样会导致O(nmavg_suffixlength),常数涉及IO,所以不可忽略。 nm ≅ 7k*1k ≅ 7m 的糟糕计划。
  • 感谢您的精彩解释!试用后我会更新你的
  • 对不起,我没有注意到行尾,我已经更改为 unix 并且工作了。非常感谢!
【解决方案3】:

由于您没有具体说明您的问题是什么或您尝试过什么,这里有一些伪代码可以帮助您入门:

foreach line in csvfile {
   code = get first column(line)
   foreach filename in folder {
      if(filename is a substring of code) { 
         //match!
         write to file ("code, filename")
         break;
      }
   }
}

【讨论】:

  • 非常感谢,有助于专注于算法的伪代码。我的问题是对使用 bash 或类似的 unix 读写文件的命令缺乏了解。
猜你喜欢
  • 2013-12-24
  • 2012-06-20
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 2018-08-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多