如何防止Google表单将表单输入转换为科学计数格式
How to prevent Google Forms from converting form input to scientific notation format
我有一个简单的脚本设置,它根据 Google 表单条目发送电子邮件,使用基于脚本的 VLookup 获取联系人电子邮件。在某些情况下,Google 表单会将输入到表单字段中的较长数字转换为科学记数法。我一直在使用的一种解决方法是在数字前输入一个撇号——出于某种原因,这会使单元格格式保持为纯文本。我想找到一个不需要这个额外步骤的解决方案。
sheet 的表单只有一个字段 eGCs。 eGCs 字段可以包含字母和数字的任意组合,并且可以是多行字符串。该脚本向 onFormSubmit 用户发送一封电子邮件,电子邮件正文中包含 eGCs 字段条目。当我尝试提交一个很长的字符串,它只是数字并且表单输入变量被转换为科学记数法时,问题就出现了。
我需要用户在 eGCs 字段中输入的内容与他们在响应 1 sheet 和发送的电子邮件正文中输入的内容完全相同。这是代码:
function onFormSubmit(e) {
var eGCs = e.values[1];
var email = Session.getActiveUser().getEmail();
//Replace the Google Sheets formatted line breaks with HTML line breaks so they display properly in the email:
eGCs = eGCs.replace(/\n/g, '<br>');
//Send the email:
var subject = "This is only a test";
var body = eGCs;
MailApp.sendEmail(email, subject, body, {htmlBody: body})
return
}
如果我提交
6110523527643880
...进入表格,数字将更改为科学记数法,并在 sheet 和发送的电子邮件中显示为 6.11052E+15
。如果我提交多行字符串,例如:
6110523527643880
6110523527643880
6110523527643880
...然后脚本工作正常并且表单字段条目未转换(可能是因为 Google 不再将其视为数字)。无论表单条目是单行还是多行,我都需要它完全按照输入显示。
这是我的example sheet / script / form。应该是public,欢迎大家测试
我假设 eGCs 是带有数字的响应。
e.values[2] 将始终以字符串形式返回(在本例中为“6.15312E+16”),因此您无法将其转换为原始数字,因为您丢失了最后一个 2 之后的所有内容。即使你转换它,你能得到的最好的是“61531200000000000”
相反,您可以从电子表格中提取值。
在 onFormSubmit() 函数的开头添加此代码:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form Responses 1");
var eGCs = sheet.getRange(sheet.getLastRow(), 2, 1, 1).getValue();
try {
eGCs = eGCs.toFixed();
} catch (e) {
Logger.log(eGCs);
}
如果是数字,您的 eGCs 现在将 return 作为完整数字,否则它将 return 作为文本。
如果您希望电子表格在提交新回复时具有正确的格式,请将此添加到您的代码中:
sheet.getRange(sheet.getLastRow(), 2, 1, 1).setNumberFormat("000");
这会将表单添加的新行转换为正确的格式。这不会影响电子表格中的实际值,只会影响其格式化方式。
表单响应 在表单(相对于电子表格)中将响应存储为字符串。您的触发函数可以获取表单的响应以获取响应者输入的字符串。
function onFormSubmit(e) {
// Get response sheet. First version works only in contained script,
// second works even in stand-alone scripts.
// var sheet = SpreadsheetApp.getActiveSheet();
var sheet = e.range.getSheet();
// Get URL of associated form & open it
var formUrl = sheet.getParent().getFormUrl();
var form = FormApp.openByUrl(formUrl);
// Get response matching the timestamp in this event
var timestamp = new Date(e.namedValues.Timestamp);
// NOTE: There is a race condition between the updates in Forms and Sheets.
// Sometimes (often!) the Spreadsheet Form Submission trigger function is invoked
// before the Forms database has completed persisting the new Responses. As
// a result, we might get no results when asking for the most recent response.
// To work around that, we will wait and try again.
var timeToGiveUp = 0;
do {
if (timeToGiveUp > 0) Utilities.sleep(1000); // sleep 1s on subsequent tries
timeToGiveUp++;
var responses = form.getResponses(timestamp);
} while (responses.length == 0 && (timeToGiveUp < 3));
Logger.log("time to give up "+timeToGiveUp);
var response = responses[0]; // assume just one response matches timestamp
var itemResponses = response.getItemResponses();
var eGCsItemNumber = 1; // Indicates where the question appears in the form
var eGCs = itemResponses[eGCsItemNumber-1].getResponse().toString();
// You now have exactly what the respondent typed, as a string.
// It can be used as-is in an email, for example.
var body = "The user entered: "+eGCs;
MailApp.sendEmail(
Session.getActiveUser().getEmail(),
"This is only a test",
body
);
// To preserve the value in the spreadsheet, we must
// force it to remain a string by prepending a tick (')
var eGCsCol = 2;
e.range.offset(0,eGCsCol-1,1,1).setValue("'"+eGCs);
}
注意 wrt 竞争条件 评论:代码的这个区域需要的实际工作是一行:
var responses = form.getResponses(timestamp);
在玩这个的时候,我发现我经常收到一个异常,就像这个答案下面的评论中提到的一样...
Cannot find method getResponses(object)
事实证明,这只发生在函数被表单提交事件触发时,而不是当 运行 来自 editor/debugger 和模拟事件时。这意味着,在短时间内,我们尝试处理的响应不会通过调用 getResponses()
返回。
由于共享文档的实施方式,任何更改都存在 传播延迟...这是在资产的一个视图中进行更改所需的时间传播到所有其他视图。
在这种情况下,我们的触发器函数已启动电子表格事件,然后打开表单视图并尝试读取 在 该视图包含它们之前的最新响应。
一个简单的解决方法是 sleep()
一段时间,以便完成传播。
Utilities.sleep(5000); // 5s pause
var responses = form.getResponses(timestamp);
简单,是的 - 但效率低下,因为即使我们不需要等待,我们也会等待。第二个问题是确定多长时间才足够……如果明天改变了怎么办?
仅当第一次不成功时,所选的解决方法才会重试获取响应。它只会在重试时等待。而且它不会永远等待 - 通过 timeToGiveUp
应用了 限制条件 。 (我们可以在循环后添加额外的成功检查,但是如果我们超过了时间限制,下一条语句将通过异常处理,我们可以让它完成脏工作。)
var timeToGiveUp = 0;
do {
if (timeToGiveUp > 0) Utilities.sleep(1000); // sleep 1s on subsequent tries
timeToGiveUp++;
var responses = form.getResponses(timestamp);
} while (responses.length == 0 && (timeToGiveUp < 3));
不止一行代码,但更健壮。
我有一个简单的脚本设置,它根据 Google 表单条目发送电子邮件,使用基于脚本的 VLookup 获取联系人电子邮件。在某些情况下,Google 表单会将输入到表单字段中的较长数字转换为科学记数法。我一直在使用的一种解决方法是在数字前输入一个撇号——出于某种原因,这会使单元格格式保持为纯文本。我想找到一个不需要这个额外步骤的解决方案。
sheet 的表单只有一个字段 eGCs。 eGCs 字段可以包含字母和数字的任意组合,并且可以是多行字符串。该脚本向 onFormSubmit 用户发送一封电子邮件,电子邮件正文中包含 eGCs 字段条目。当我尝试提交一个很长的字符串,它只是数字并且表单输入变量被转换为科学记数法时,问题就出现了。
我需要用户在 eGCs 字段中输入的内容与他们在响应 1 sheet 和发送的电子邮件正文中输入的内容完全相同。这是代码:
function onFormSubmit(e) {
var eGCs = e.values[1];
var email = Session.getActiveUser().getEmail();
//Replace the Google Sheets formatted line breaks with HTML line breaks so they display properly in the email:
eGCs = eGCs.replace(/\n/g, '<br>');
//Send the email:
var subject = "This is only a test";
var body = eGCs;
MailApp.sendEmail(email, subject, body, {htmlBody: body})
return
}
如果我提交
6110523527643880
...进入表格,数字将更改为科学记数法,并在 sheet 和发送的电子邮件中显示为 6.11052E+15
。如果我提交多行字符串,例如:
6110523527643880
6110523527643880
6110523527643880
...然后脚本工作正常并且表单字段条目未转换(可能是因为 Google 不再将其视为数字)。无论表单条目是单行还是多行,我都需要它完全按照输入显示。
这是我的example sheet / script / form。应该是public,欢迎大家测试
我假设 eGCs 是带有数字的响应。
e.values[2] 将始终以字符串形式返回(在本例中为“6.15312E+16”),因此您无法将其转换为原始数字,因为您丢失了最后一个 2 之后的所有内容。即使你转换它,你能得到的最好的是“61531200000000000”
相反,您可以从电子表格中提取值。 在 onFormSubmit() 函数的开头添加此代码:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form Responses 1");
var eGCs = sheet.getRange(sheet.getLastRow(), 2, 1, 1).getValue();
try {
eGCs = eGCs.toFixed();
} catch (e) {
Logger.log(eGCs);
}
如果是数字,您的 eGCs 现在将 return 作为完整数字,否则它将 return 作为文本。
如果您希望电子表格在提交新回复时具有正确的格式,请将此添加到您的代码中:
sheet.getRange(sheet.getLastRow(), 2, 1, 1).setNumberFormat("000");
这会将表单添加的新行转换为正确的格式。这不会影响电子表格中的实际值,只会影响其格式化方式。
表单响应 在表单(相对于电子表格)中将响应存储为字符串。您的触发函数可以获取表单的响应以获取响应者输入的字符串。
function onFormSubmit(e) {
// Get response sheet. First version works only in contained script,
// second works even in stand-alone scripts.
// var sheet = SpreadsheetApp.getActiveSheet();
var sheet = e.range.getSheet();
// Get URL of associated form & open it
var formUrl = sheet.getParent().getFormUrl();
var form = FormApp.openByUrl(formUrl);
// Get response matching the timestamp in this event
var timestamp = new Date(e.namedValues.Timestamp);
// NOTE: There is a race condition between the updates in Forms and Sheets.
// Sometimes (often!) the Spreadsheet Form Submission trigger function is invoked
// before the Forms database has completed persisting the new Responses. As
// a result, we might get no results when asking for the most recent response.
// To work around that, we will wait and try again.
var timeToGiveUp = 0;
do {
if (timeToGiveUp > 0) Utilities.sleep(1000); // sleep 1s on subsequent tries
timeToGiveUp++;
var responses = form.getResponses(timestamp);
} while (responses.length == 0 && (timeToGiveUp < 3));
Logger.log("time to give up "+timeToGiveUp);
var response = responses[0]; // assume just one response matches timestamp
var itemResponses = response.getItemResponses();
var eGCsItemNumber = 1; // Indicates where the question appears in the form
var eGCs = itemResponses[eGCsItemNumber-1].getResponse().toString();
// You now have exactly what the respondent typed, as a string.
// It can be used as-is in an email, for example.
var body = "The user entered: "+eGCs;
MailApp.sendEmail(
Session.getActiveUser().getEmail(),
"This is only a test",
body
);
// To preserve the value in the spreadsheet, we must
// force it to remain a string by prepending a tick (')
var eGCsCol = 2;
e.range.offset(0,eGCsCol-1,1,1).setValue("'"+eGCs);
}
注意 wrt 竞争条件 评论:代码的这个区域需要的实际工作是一行:
var responses = form.getResponses(timestamp);
在玩这个的时候,我发现我经常收到一个异常,就像这个答案下面的评论中提到的一样...
Cannot find method getResponses(object)
事实证明,这只发生在函数被表单提交事件触发时,而不是当 运行 来自 editor/debugger 和模拟事件时。这意味着,在短时间内,我们尝试处理的响应不会通过调用 getResponses()
返回。
由于共享文档的实施方式,任何更改都存在 传播延迟...这是在资产的一个视图中进行更改所需的时间传播到所有其他视图。
在这种情况下,我们的触发器函数已启动电子表格事件,然后打开表单视图并尝试读取 在 该视图包含它们之前的最新响应。
一个简单的解决方法是 sleep()
一段时间,以便完成传播。
Utilities.sleep(5000); // 5s pause
var responses = form.getResponses(timestamp);
简单,是的 - 但效率低下,因为即使我们不需要等待,我们也会等待。第二个问题是确定多长时间才足够……如果明天改变了怎么办?
仅当第一次不成功时,所选的解决方法才会重试获取响应。它只会在重试时等待。而且它不会永远等待 - 通过 timeToGiveUp
应用了 限制条件 。 (我们可以在循环后添加额外的成功检查,但是如果我们超过了时间限制,下一条语句将通过异常处理,我们可以让它完成脏工作。)
var timeToGiveUp = 0;
do {
if (timeToGiveUp > 0) Utilities.sleep(1000); // sleep 1s on subsequent tries
timeToGiveUp++;
var responses = form.getResponses(timestamp);
} while (responses.length == 0 && (timeToGiveUp < 3));
不止一行代码,但更健壮。