【问题标题】:Apps Script Utilities.parseCsv assumes new row on line breaks within double quotesApps Script Utilities.parseCsv 在双引号内的换行符上假定新行
【发布时间】:2016-04-16 00:22:34
【问题描述】:

当使用Utilities.parseCsv() 时,包含在双引号内的换行符被假定为完全是新行。此函数的输出数组将有几个不正确的行。

我该如何解决这个问题,或者解决它?

编辑:具体来说,我可以转义只存在于双引号内的换行符吗?即。

/r/n "I have some stuff to do:/r/n Go home/r/n Take a Nap"/r/n

将转义到:

/r/n "I have some stuff to do://r//n Go home//r//n Take a Nap"/r/n

Edit2:2012 年的错误报告:https://code.google.com/p/google-apps-script-issues/issues/detail?id=1871

【问题讨论】:

  • parseCsv() 方法接受一个字符串。您可以使用字符串方法来消除导致问题的原因。也许全球replace() 会起作用? var newCsv = myCsv.replace(/\n/g, "") //replace new line with empty space
  • 我已经编辑了我的问题以澄清。遗憾的是,这样做也会删除任何新行的指示,这些新行由/r/n 分隔。大多数电子表格应用程序会注意到双引号中存在换行符并将其忽略。 parseCsv() 函数在解析换行符时似乎没有使双引号生效。

标签: google-apps-script


【解决方案1】:

所以我有一个有点大的 csv 文件,大约 10MB 50k 行,它在每行末尾包含一个字段,其中包含用户输入的各种字符的 cmets。当我测试一小组行时,我发现建议的正则表达式解决方案正在工作,但是当我将大文件扔给它时,再次出现错误,在使用正则表达式尝试了一些事情之后,我什至让整个运行时崩溃.

顺便说一句,我在 V8 运行时运行我的代码。

在摸索了大约一个小时后,AppsSript 运行时的错误消息并没有真正有用。我有一个想法,如果一些奇怪的用户决定以一些奇怪的方式使用反斜杠,从而导致一些转义出错,该怎么办。 所以我尝试用其他东西替换我数据中的所有反斜杠一段时间,直到我得到 parseCsv() 返回的数组。 有效! 我的假设是在行尾有一个 \ 会破坏替换。

所以我的最终解决方案是:

function testParse() {
    let csv =
        '"title1","title2","title3"\r\n' +
        '1,"person1","A ""comment"" with a \\ and \\\r\n a second line"\r\n' +
        '2,"person2","Another comment"';

    let sanitizedString =
        csv.replace(/\\/g, '::back-slash::')
            .replace(/(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]\r?\n(?:\\[\s\S][^'\\]\r?\n)*')/g,
                match => match.replace(/\r?\n/g, "::newline::"));
    let arr = Utilities.parseCsv(sanitizedString);
    for (let i = 0, rows = arr.length; i < rows; i++) {
        for (let j = 0, cols = arr[i].length; j < cols; j++) {
            arr[i][j] = 
                arr[i][j].replace(/::back-slash::/g,'\\')
                    .replace(/::newline::/g,'\r\n');

        }
    }
    Logger.log(arr)
}

输出:

[20-02-18 11:29:03:980 CST] [[title1, title2, title3], [1, person1, A "comment" with a \ and \
 a second line], [2, person2, Another comment]]

【讨论】:

    【解决方案2】:

    使用Sheets API 可能会对您有所帮助。 就我而言,它可以正常工作,而无需替换包含双引号多行文本的 CSV 文本。

    首先,您需要确保以下几点:

    启用高级服务

    要使用高级 Google 服务,请按照以下说明操作:

    1. 在脚本编辑器中,选择资源 > 高级 Google 服务...
    2. 在出现的高级 Google 服务对话框中, 点击您要使用的服务旁边的开/关开关。
    3. 在对话框中单击确定

    如果没问题,您可以将 CSV 文本数据导入工作表:

    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = ss.getSheetByName('some_name');
    const resource = {
        requests: [
            {
                pasteData: {
                    data: csvText, // Your CSV data string
                    coordinate: {sheetId: sheet.getSheetId()},
                    delimiter: ",",
                }
            }
    
        ]
    };
    Sheets.Spreadsheets.batchUpdate(resource, ss.getId());
    

    或者对于TypeScript,clasp可以使用:

    var ss = SpreadsheetApp.getActiveSpreadsheet();
    var sheet = ss.getSheetByName('some_name');
    const resource: GoogleAppsScript.Sheets.Schema.BatchUpdateSpreadsheetRequest = {
        requests: [
            {
                pasteData: {
                    data: csvText, // Your CSV data string
                    coordinate: {sheetId: sheet.getSheetId()},
                    delimiter: ",",
                }
            }
    
        ]
    };
    Sheets.Spreadsheets.batchUpdate(resource, ss.getId());
    

    【讨论】:

    • 这仍然是 2021 年的正确答案,2012 年的 Scripts 错误尚未修复。
    【解决方案3】:

    为了避免试图理解正则表达式,我在下面找到了一个解决方法,而不是使用Utilities.parseCsv()。我正在逐行复制数据。

    事情是这样的:

    如果您能找到一种方法在您的 CSV 末尾添加一个额外的列,该列始终包含完全相同的 ,那么您可以强制使用特定的“换行符”到那个

    然后,将整行复制到 A 列并使用谷歌应用脚​​本的专用 splitTextToColumns() 方法...

    在下面的示例中,我从 HTML 表单中获取 CSV。这很有效,因为我还拥有对用户从中获取 CSV 的数据库的管理员访问权限,因此我可以强制所有 CSV 文件的最后一列...

    function updateSheet(form) {
      var fileData = form.myFile;
      // gets value from form
      blob = fileData.getBlob();
      var name = String(form.folderId);
      // gets value from form
    
      var ss = SpreadsheetApp.getActiveSpreadsheet();
      var sheet = ss.setActiveSheet(ss.getSheetByName(name), true);
    
      sheet.clearContents().clearFormats();
    
      var values = [];
      // below, the "Dronix" value is the value that I could force at the end of each row
      var rows = blob.contents.split('"Dronix",\n');
    
      if (rows.length > 1) {
        for (var r = 2, max_r = rows.length; r < max_r; ++r) {
          sheet.getRange(r + 6, 1, 1, 1).setValue(String(rows[r]));
        }
      }
    
      var spreadsheet = SpreadsheetApp.getActive();
      spreadsheet.getRange("A:A").activate();
      spreadsheet.getRange("A:A").splitTextToColumns();
    }
    

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,终于解决了。感谢 Douglas 的正则表达式/代码(我必须说有点过头了),它与相关领域非常匹配。不幸的是,这只是成功的一半。显示的替换将简单地将整个字段替换为\r\n。因此,仅当 CSV 文件中的 "" 之间的内容仅为 \r\n 时才有效。如果它与其他数据一起嵌入到该字段中,它会默默地破坏该数据。要解决问题的另一半,您需要使用一个函数作为替代。替换将匹配字段作为参数,因此您可以在函数中执行简单的替换调用来处理该字段。示例...

      数据:

      "Student","Officer
      

      研发

      Special Member","Member",705,"2016-07-25 22:40:04 EDT"
      

      要处理的代码:

      var dataString = myBlob().getDataAsString(); 
      var escapedString = dataString.replace(/(?=["'])(?:"[^"\](?:\[\s\S][^"\])"|'[^'\]\r\n(?:\[\s\S][^'\]\r\n)')/g, function(match) { return match.replace(/\r\n/g,"\r\n")} ); 
      var csvData = Utilities.parseCsv(escapedString);
      

      现在"Officer\r\nRD\r\nSpecial Member" 字段被单独评估,因此替换函数中的 match.replace 调用可以非常直接和简单。

      【讨论】:

      • 如果它已经是双引号字段,则替换将替换整个字段。在这种情况下,它旨在用转义换行符替换未转义的换行符。我每天在一些自动解析脚本中使用它。如果尝试在已经逃逸的字段上使用它可能会产生意想不到的后果(我想,已经有一段时间了)。 TBH,我对正则表达式没有深入的了解。我确实在 C# 中制作了一个强大的 CSV 解析器,它是无正则表达式的,我可能会考虑将它移植到 JavaScript。
      【解决方案5】:

      从另一个帖子的另一个回复中检索并稍微修改了一个正则表达式:https://stackoverflow.com/a/29452781/3547347

      正则表达式:(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]\r\n(?:\\[\s\S][^'\\]\r\n)*')

      代码:

        var dataString = myBlob.getDataAsString();
        var escapedString = dataString.replace(/(?=["'])(?:"[^"\\]*(?:\\[\s\S][^"\\]*)*"|'[^'\\]\r\n(?:\\[\s\S][^'\\]\r\n)*')/g, '\\r\\n');
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-20
        • 1970-01-01
        • 2019-11-27
        • 2015-05-22
        • 1970-01-01
        相关资源
        最近更新 更多