【问题标题】:How to remove newlines inside csv cells using regex/terminal tools?如何使用正则表达式/终端工具删除 csv 单元格内的换行符?
【发布时间】:2016-03-03 19:57:32
【问题描述】:

我有一个 csv 文件,其中一些单元格中有换行符。例如:

id,name 
01,"this is
with newline"
02,no newline 

我想删除单元格内的所有换行符。

在事先不知道列数的情况下,一般如何使用正则表达式或其他终端工具来做到这一点?

【问题讨论】:

  • 是否可以调整创建文件的过程以从列中删除换行符文件实际创建/weitten 之前?
  • 当然,@ray 如果可行的话
  • “如果有效”是什么意思?如果您更新创建文件的过程,它将起作用。在程序将行写入文件之前,您希望从行中的每一列中删除换行符。
  • 我只是想说“如果它有效,那没关系”。请把你的答案写成代码。这将更容易测试。
  • 介意发布您到目前为止编写的代码,即首先负责创建文件的代码吗?这将是一个很好的起点。

标签: regex perl unix sed terminal


【解决方案1】:

使用 notepadd++ 打开 *csv 文件,然后按 Ctrl+H。转到选项卡替换并输入搜索框“换行符”,然后写入以替换要替换的单词,或者如果需要,将其留空。

【讨论】:

  • 我想自动化这个过程。因此我只想使用终端工具。
  • 如何创建 csv 文件?使用 Java 还是手动?
  • @LefterisBab:他想自动化这个过程。如果文件是“手动”创建的,他可能会在保存文件之前手动删除不需要的换行符。他还提到他只想使用终端工具,所以你建议启动 Notepad++ 或其他一些非 shell 程序是没有用的。
  • 所以@ray 这已经在stackoverflow中了。 stackoverflow.com/questions/1251999/…
  • 否 @LefterisBab 该解决方案将替换所有换行符,无论它是否在单元格内。
【解决方案2】:

这实际上是一个比看起来更难的问题,在我看来,这意味着regex 不是正确的解决方案。因为您正在处理引用/转义的字符串,跨越多个“行”,您最终会得到一个复杂且难以阅读的regex。 (这不是不可能的,只是很乱)。

我建议改为使用解析器。 Perl 在Text::CSV 中有一个,它有点像这样:

#!/usr/bin/env perl

use strict;
use warnings;

use Text::CSV;

my $csv = Text::CSV->new( { binary => 1, eol => "\n" } );

while ( my $row = $csv->getline( \*ARGV ) ) {
    s/\n/ /g for @$row;
    $csv->print( \*STDOUT, $row );
}

这会将文件作为管道输入/在命令行中指定 - 这就是 \*ARGV 所做的 - 它是一个特殊的文件句柄,可让您做......基本上是 sed 所做的:

somecommand.sh | myscript.pl
myscript.pl filename_to_process

ARGV 文件句柄会自动执行。 (如果您愿意,可以显式打开文件或使用\*STDIN

【讨论】:

    【解决方案3】:

    在事先不知道列数的情况下,一般如何使用正则表达式或其他终端工具来做到这一点?

    我不认为正则表达式是最合适的方法,并且最终可能会变得相当复杂。相反,我认为从长远来看,处理文件的单独程序可能更容易维护。

    由于您可以使用任何终端工具,因此我选择了 python,代码如下:

    #!/usr/bin/python3 -B
    
    import csv
    import sys
    
    with open(sys.argv[1]) as csvfile:
        reader = csv.reader(csvfile)
        for row in reader:
            stripped = [col.replace('\n', ' ') for col in row]
            print(','.join(stripped))
    

    我觉得上面的代码非常直观易懂,不需要复杂的正则表达式。

    这里的输入文件有以下内容:

    id,name
    01,"this is
    with newline"
    02,no newline
    

    为了证明它有效,它的输出复制如下:

    ➜  ~  ./test.py input.csv
    id,name
    01,this is with newline
    02,no newline
    

    您可以从其他程序调用 python 脚本并将文件名提供给它。如果你真的需要的话,你只需要为 python 程序添加一个小的更新来写出文件。

    我已将换行符替换为空格以避免可能不需要的连接(例如this iswith newline),但您可以将换行符替换为您想要的任何内容,包括空字符串''

    【讨论】:

      【解决方案4】:

      如果您正在使用这个xlsx2csv 工具,它有这个选项:

      -e, --escape          Escape \r\n\t characters
      

      使用它,然后根据需要替换\n,比如(如果\n应该替换为空字符串):

      sed 's/\\n//g' filein.csv` > fileout.csv
      

      一次性完成:

      PATH/TO/xlsx2csv.py -e filein.xlsx | sed 's/\\n//g' > fileout.csv
      

      【讨论】:

      • 我认为在尝试将其发布为答案之前询问/确认这是 OP 正在使用的工具会更好。很难判断这个答案是否有用。
      【解决方案5】:

      我怀疑您实际上不想删除换行符,而是用空格替换它。如果您的输入文件看起来很简单,那么您应该这样做:

      $ awk '{ORS=( (c+=gsub(/"/,"&"))%2 ? FS : RS )} 1' file
      id,name
      01,"this is with newline"
      02,no newline
      

      【讨论】:

        【解决方案6】:

        我已经编写了一个方法来删除单元格内嵌入的新行。下面的方法返回一个 java.util.List 对象,其中包含 CSV 文件中的所有行

        List<String> getAllRowsInCSVFileAsList(File selectedCSVFile){
          FileReader fileReader = null;
          BufferedReader reader = null;
          List<String> values = new ArrayList<String>();
          try{
              fileReader = new FileReader(selectedCSVFile);
              reader = new BufferedReader(fileReader);
              String line = reader.readLine();
              String previousLine = "";
              //
              boolean intendLineInCell = false;
              while(line != null){
                  if(intendLineInCell){
                      if(line.indexOf("\"") != -1 && line.indexOf("\"") == line.lastIndexOf("\"")){
                          previousLine += line;
                          values.add(previousLine);
                          previousLine = "";
                          intendLineInCell = false;
                      } else if(line.indexOf("\"") != -1 && line.indexOf("\"") != line.lastIndexOf("\"")){
                          if(getTotalNumberOfCharacterSequenceOccurrenceInString("\"", line) % 2 == 0){
                              previousLine += line;
                          }else{
                              previousLine += line;
                              values.add(previousLine);
                              previousLine = "";
                              intendLineInCell = false;
                          }
                      } else{
                          previousLine += line;
                      }
                  }else{
                      if(line.indexOf("\"") == -1){
                          values.add(line);
                      }else if ((line.indexOf("\"") == line.lastIndexOf("\"")) && line.indexOf("\"") != -1){
                          intendLineInCell = true;
                          previousLine = line;
                      }else if(line.indexOf("\"") != line.lastIndexOf("\"") && line.indexOf("\"") != -1){
                          values.add(line);
                      }
                  }
                  line = reader.readLine();
              }
          }catch(IOException ie){
              ie.printStackTrace();
          }finally{
              if(fileReader != null){
                  try {
                      fileReader.close();
                  } catch (IOException e) {
                      e.printStackTrace();
                  }
              }
              if(reader != null){
                  try {
                      reader.close();
                  } catch (IOException e) {
                    e.printStackTrace();
                  }
              }
          }
          return values;
        

        }

        int getTotalNumberOfCharacterSequenceOccurrenceInString(String characterSequence, String text){
          int count = 0;
          while(text.indexOf(characterSequence) != -1){
              text = text.replaceFirst(characterSequence, "");
              count++;
          }
          return count;
        

        }

        假设您正在创建一个包含一行和五列的 csv 文件,并且在第 4 个单元格中您有一个嵌入的新行(在单元格内输入)

        您的数据将如下所示(实际上我们在 csv 中只有一行,但如果您在记事本中打开它看起来像 2 行)。

        dinesh,kumar,24,"23 
        tambaram india",green
        

        如果单元格内有输入,可能如下所示

        "23
        tambaram india"
        

        该单元格以双引号(“)开头并以双引号(”)结尾。

        如果有双引号("),则在读取行时通过使用双引号("),我们可以理解单元格内有一个嵌入的输入。

        代码将下一行与该行连接并检查是否有结束双引号(“)。如果有,它会在 java.util.List 对象中添加一个新行,否则它会连接下一行并检查它是否有结束双引号(“)等等。这里我已经解释了一个单元格,但是如果该行有很多带有嵌入式输入的单元格,该方法也适用。

        【讨论】:

          猜你喜欢
          • 2013-11-06
          • 1970-01-01
          • 2010-10-22
          • 1970-01-01
          • 2011-07-01
          • 1970-01-01
          • 2013-04-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多