IE:之前/:在td的100%高度问题之后(IE :before/:after 100% height issues on td)
有没有人有关于如何修复(表观)IE渲染错误的想法:before /:td后的高度?
从它的外观来看,IE似乎只认为:在伪元素之前/之后,它与父元素的
<TD>
内容一样高。 如果<TD>
#1是x2行高w内容且<TD>
#2只有x1行内容,则IE只会将<TD>
#2的高度之前/之后的值显示为高于x1行值的内容。我在这里创建了一个小提琴示例,以更好地说明我的问题: http : //jsfiddle.net/231gnfpz/注意:我添加了一个红色的背景:before /:after更好地帮助在IE中可视化我的问题
在我的例子中,我有一个中间的
<TD>
,我在之前/之后应用了一个:在之后尝试创建一个到特定列外部的盒子阴影。 它的一个旧项目,所以我没有访问重写整个表,FireFox / Chrome似乎没有这个问题,IE8-11似乎与我有同样的问题:之前/:有一个身高:100% 。码:
table { background: grey; width: 100%; } table td { text-align: center; vertical-align: top; background: white; padding: 4px; width: 33.33%; position: relative; } .testTD:before { box-shadow: -15px 0 15px -15px grey inset; content: " "; height: 100%; left: -15px; position: absolute; top: 0; width: 15px; background: Red; } .testTD:after { z-index: 1; box-shadow: 15px 0 15px -15px grey inset; content: " "; height: 100%; right: -15px; position: absolute; top: 0; width: 15px; background: Red; }
<table cellspacing="1" cellpadding "0" border="0"> <tr> <td>test1</td> <td class="testTD">test1</td> <td>test1</td> </tr> <tr> <td>test2 <br/>test2 </td> <td class="testTD">test2</td> <td>test2</td> </tr> <tr> <td>test3 <br/>test3 <br/>test3 </td> <td class="testTD">test3</td> <td>test3</td> </tr> </table>
Does anyone have an idea on how to fix an (apparent) IE rendering bug with the :before/:after heights on a td?
From the looks of it, IE seems to only think a :before/:after pseudo element is as tall as the parent's
<TD>
content inside of it. If<TD>
#1 is x2 lines tall w content and<TD>
#2 has only x1 line of content, IE will only render the :before/:after height of<TD>
#2 to be as tall as x1 line's worth of content.I created a fiddle example here to better illustrate my problem: http://jsfiddle.net/231gnfpz/ note: I added a red background to the :before/:after to better help visualize my problem in IE
In my example I have a middle
<TD>
that I applied a :before/:after to try to create a box-shadow to the outside of the specific column. Its an old project so I don't have access to rewrite the entire table, FireFox/Chrome seem to have no issue with this, IE8-11 appear to have the same problem with my :before/:after having a height:100%.Code:
table { background: grey; width: 100%; } table td { text-align: center; vertical-align: top; background: white; padding: 4px; width: 33.33%; position: relative; } .testTD:before { box-shadow: -15px 0 15px -15px grey inset; content: " "; height: 100%; left: -15px; position: absolute; top: 0; width: 15px; background: Red; } .testTD:after { z-index: 1; box-shadow: 15px 0 15px -15px grey inset; content: " "; height: 100%; right: -15px; position: absolute; top: 0; width: 15px; background: Red; }
<table cellspacing="1" cellpadding "0" border="0"> <tr> <td>test1</td> <td class="testTD">test1</td> <td>test1</td> </tr> <tr> <td>test2 <br/>test2 </td> <td class="testTD">test2</td> <td>test2</td> </tr> <tr> <td>test3 <br/>test3 <br/>test3 </td> <td class="testTD">test3</td> <td>test3</td> </tr> </table>
原文:https://stackoverflow.com/questions/27911771
最满意答案
我发现这个问题非常有趣,特别是因为我在Ado.Net和EF 6上使用
async
。我希望有人给出这个问题的解释,但并没有发生。 所以我试图重现这个问题在我身边。 我希望你们中有人会发现这个有趣的。第一个好消息:我转载它:)和差异是巨大的。 有一个因子8 ...
首先,我怀疑与
CommandBehavior
有关的事情,因为我读了一篇有关与Adoasync
的有趣的文章 ,他说:“由于非顺序访问模式必须存储整行的数据,如果从服务器读取大列(如varbinary(MAX),varchar(MAX),nvarchar(MAX))或XML )“。
我怀疑
ToList()
调用是CommandBehavior.SequentialAccess
和异步的CommandBehavior.Default
是CommandBehavior.Default
(非连续的,这可能会导致问题)。 所以我下载了EF6的来源,并将破解点放在任何地方(当然是使用CommandBehavior
地方)。结果: 没有 。 所有的调用都是使用
CommandBehavior.Default
....所以我试图进入EF代码来了解会发生什么...和.. ooouch ...我从来没有看到这样一个委托代码,一切似乎都懒惰执行...所以我试图做一些分析来了解会发生什么
我想我有东西
这里是创建我的基准测试表的模型,其中有3500行,每个
varbinary(MAX)
256 Kb的随机数据。 (EF 6.1 - CodeFirst - CodePlex ):public class TestContext : DbContext { public TestContext() : base(@"Server=(localdb)\\v11.0;Integrated Security=true;Initial Catalog=BENCH") // Local instance { } public DbSet<TestItem> Items { get; set; } } public class TestItem { public int ID { get; set; } public string Name { get; set; } public byte[] BinaryData { get; set; } }
这里是我用来创建测试数据的代码,以及基准的EF。
using (TestContext db = new TestContext()) { if (!db.Items.Any()) { foreach (int i in Enumerable.Range(0, 3500)) // Fill 3500 lines { byte[] dummyData = new byte[1 << 18]; // with 256 Kbyte new Random().NextBytes(dummyData); db.Items.Add(new TestItem() { Name = i.ToString(), BinaryData = dummyData }); } await db.SaveChangesAsync(); } } using (TestContext db = new TestContext()) // EF Warm Up { var warmItUp = db.Items.FirstOrDefault(); warmItUp = await db.Items.FirstOrDefaultAsync(); } Stopwatch watch = new Stopwatch(); using (TestContext db = new TestContext()) { watch.Start(); var testRegular = db.Items.ToList(); watch.Stop(); Console.WriteLine("non async : " + watch.ElapsedMilliseconds); } using (TestContext db = new TestContext()) { watch.Restart(); var testAsync = await db.Items.ToListAsync(); watch.Stop(); Console.WriteLine("async : " + watch.ElapsedMilliseconds); } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess); while (await reader.ReadAsync()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReaderAsync SequentialAccess : " + watch.ElapsedMilliseconds); } } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = await cmd.ExecuteReaderAsync(CommandBehavior.Default); while (await reader.ReadAsync()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReaderAsync Default : " + watch.ElapsedMilliseconds); } } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess); while (reader.Read()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReader SequentialAccess : " + watch.ElapsedMilliseconds); } } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = cmd.ExecuteReader(CommandBehavior.Default); while (reader.Read()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReader Default : " + watch.ElapsedMilliseconds); } }
对于常规EF调用(
.ToList()
),分析似乎是“正常”,易于阅读:在这里,我们发现与秒表相比,我们拥有8.4秒的时间(分析速度减慢了perfs)。 我们还在呼叫路径上找到HitCount = 3500,女巫在测试中与3500线一致。 在TDS解析器端,事情开始变得更糟,因为我们在
TryReadByteArray()
方法中读取了118 353个调用,这是缓冲循环发生的。 (平均33.8调用256kb的每个byte[]
对于
async
情况,它真的是不同的....首先,.ToListAsync()
调用安排在ThreadPool上,然后等待。 这里没什么惊喜 但是,现在,这里是ThreadPool中的async
地狱:首先,在第一种情况下,我们沿着完整的通话路径只有3500次点击,这里我们有118 371.此外,你必须想象所有的同步电话,我没有放在屏幕上...
第二,在第一种情况下,我们对
TryReadByteArray()
方法进行了“只有118 353”的调用,这里我们有2 050 210个调用! 它是17倍以上(在大型1Mb阵列的测试中,它是160倍)此外还有:
- 创建了120 000个
Task
实例- 727 519
Interlocked
电话- 290 569
Monitor
来电- 98 283
ExecutionContext
实例,具有264 481捕获- 208 733
SpinLock
电话我的猜测是缓冲是以异步方式(而不是一个好的)进行的,并行任务试图从TDS中读取数据。 创建太多任务只是为了解析二进制数据。
作为一个初步的结论,我们可以说Async很棒,但是EF6很好,但EF6在当前实现中异步的用法在性能方面增加了一个主要的开销,即线程方面和CPU方面(12%的CPU使用率
ToList()
情况和20%的ToListAsync
情况下,工作时间为8到10倍...我运行在旧的i7 920上)。在做一些测试时,我再次考虑这篇文章 ,我注意到我想念的事情:
“对于.Net 4.5中的新异步方法,它们的行为与同步方法完全相同,除了一个显着的异常:非顺序模式下的ReadAsync。
什么 ?!!!
所以我扩展了我的基准,包括Ado.Net在常规/异步调用,并与
CommandBehavior.SequentialAccess
/CommandBehavior.Default
,这是一个惊喜! :我们与Ado.Net有完全相同的行为! 捂脸......
我的最终结论是 :在EF 6实现中有一个错误。 当对包含
binary(max)
列的表执行异步调用时,它应将CommandBehavior
切换为SequentialAccess
。 在Ado.Net方面,创建太多任务的问题会减缓进程。 EF问题是它不应该使用Ado.Net。现在你知道,而不是使用EF6异步方法,你最好以非常异步的方式调用EF,然后使用
TaskCompletionSource<T>
以异步方式返回结果。注1:我修改了我的帖子,因为一个可耻的错误....我做了我的第一次测试网络,而不是在本地,有限的带宽扭曲的结果。 以下是更新后的结果。
注2:我没有将测试扩展到其他用例(例如:
nvarchar(max)
),但有很多机会发生相同的行为。注3:对于
ToList()
情况,通常情况下,是12%的CPU(CPU的1/8 = 1逻辑内核)。 对于ToListAsync()
情况,不寻常的是最多20%,就好像Scheduler不能使用所有的Treads。 这可能是由于太多的任务创建,或者可能是TDS解析器的瓶颈,我不知道...I found this question very interesting, especially since I'm using
async
everywhere with Ado.Net and EF 6. I was hoping someone to give an explanation for this question, but it doesn't happened. So I tried to reproduce this problem on my side. I hope some of you will find this interesting.First good news : I reproduced it :) And the difference is enormous. With a factor 8 ...
First I was suspecting something dealing with
CommandBehavior
, since I read an interesting article aboutasync
with Ado, saying this :"Since non-sequential access mode has to store the data for the entire row, it can cause issues if you are reading a large column from the server (such as varbinary(MAX), varchar(MAX), nvarchar(MAX) or XML)."
I was suspecting
ToList()
calls to beCommandBehavior.SequentialAccess
and async ones to beCommandBehavior.Default
(non-sequential, which can cause issues). So I downloaded EF6's sources, and put breakpoints everywhere (whereCommandBehavior
where used, of course).Result : nothing. All the calls are made with
CommandBehavior.Default
.... So I tried to step into EF code to understand what happens... and.. ooouch... I never see such a delegating code, everything seems lazy executed...So I tried to do some profiling to understand what happens...
And I think I have something...
Here's the model to create the table I benchmarked, with 3500 lines inside of it, and 256 Kb random data in each
varbinary(MAX)
. (EF 6.1 - CodeFirst - CodePlex) :public class TestContext : DbContext { public TestContext() : base(@"Server=(localdb)\\v11.0;Integrated Security=true;Initial Catalog=BENCH") // Local instance { } public DbSet<TestItem> Items { get; set; } } public class TestItem { public int ID { get; set; } public string Name { get; set; } public byte[] BinaryData { get; set; } }
And here's the code I used to create the test data, and benchmark EF.
using (TestContext db = new TestContext()) { if (!db.Items.Any()) { foreach (int i in Enumerable.Range(0, 3500)) // Fill 3500 lines { byte[] dummyData = new byte[1 << 18]; // with 256 Kbyte new Random().NextBytes(dummyData); db.Items.Add(new TestItem() { Name = i.ToString(), BinaryData = dummyData }); } await db.SaveChangesAsync(); } } using (TestContext db = new TestContext()) // EF Warm Up { var warmItUp = db.Items.FirstOrDefault(); warmItUp = await db.Items.FirstOrDefaultAsync(); } Stopwatch watch = new Stopwatch(); using (TestContext db = new TestContext()) { watch.Start(); var testRegular = db.Items.ToList(); watch.Stop(); Console.WriteLine("non async : " + watch.ElapsedMilliseconds); } using (TestContext db = new TestContext()) { watch.Restart(); var testAsync = await db.Items.ToListAsync(); watch.Stop(); Console.WriteLine("async : " + watch.ElapsedMilliseconds); } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess); while (await reader.ReadAsync()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReaderAsync SequentialAccess : " + watch.ElapsedMilliseconds); } } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = await cmd.ExecuteReaderAsync(CommandBehavior.Default); while (await reader.ReadAsync()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReaderAsync Default : " + watch.ElapsedMilliseconds); } } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess); while (reader.Read()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReader SequentialAccess : " + watch.ElapsedMilliseconds); } } using (var connection = new SqlConnection(CS)) { await connection.OpenAsync(); using (var cmd = new SqlCommand("SELECT ID, Name, BinaryData FROM dbo.TestItems", connection)) { watch.Restart(); List<TestItem> itemsWithAdo = new List<TestItem>(); var reader = cmd.ExecuteReader(CommandBehavior.Default); while (reader.Read()) { var item = new TestItem(); item.ID = (int)reader[0]; item.Name = (String)reader[1]; item.BinaryData = (byte[])reader[2]; itemsWithAdo.Add(item); } watch.Stop(); Console.WriteLine("ExecuteReader Default : " + watch.ElapsedMilliseconds); } }
For the regular EF call (
.ToList()
), the profiling seems "normal" and is easy to read :Here we find the 8.4 seconds we have with the Stopwatch (profiling slow downs the perfs). We also find HitCount = 3500 along the call path, which is consistent with the 3500 lines in the test. On the TDS parser side, things start to became worse since we read 118 353 calls on
TryReadByteArray()
method, which is were the buffering loop occurs. (an average 33.8 calls for eachbyte[]
of 256kb)For the
async
case, it's really really different.... First, the.ToListAsync()
call is scheduled on the ThreadPool, and then awaited. Nothing amazing here. But, now, here's theasync
hell on the ThreadPool :First, in the first case we were having just 3500 hit counts along the full call path, here we have 118 371. Moreover, you have to imagine all the synchronization calls I didn't put on the screenshoot...
Second, in the first case, we were having "just 118 353" calls to the
TryReadByteArray()
method, here we have 2 050 210 calls ! It's 17 times more... (on a test with large 1Mb array, it's 160 times more)Moreover there are :
- 120 000
Task
instances created- 727 519
Interlocked
calls- 290 569
Monitor
calls- 98 283
ExecutionContext
instances, with 264 481 Captures- 208 733
SpinLock
callsMy guess is the buffering is made in an async way (and not a good one), with parallel Tasks trying to read data from the TDS. Too many Task are created just to parse the binary data.
As a preliminary conclusion, we can say Async is great, EF6 is great, but EF6's usages of async in it's current implementation adds a major overhead, on the performance side, the Threading side, and the CPU side (12% CPU usage in the
ToList()
case and 20% in theToListAsync
case for a 8 to 10 times longer work... I run it on an old i7 920).While doings some tests, I was thinking about this article again and I notice something I miss :
"For the new asynchronous methods in .Net 4.5, their behavior is exactly the same as with the synchronous methods, except for one notable exception: ReadAsync in non-sequential mode."
What ?!!!
So I extend my benchmarks to include Ado.Net in regular / async call, and with
CommandBehavior.SequentialAccess
/CommandBehavior.Default
, and here's a big surprise ! :We have the exact same behavior with Ado.Net !!! Facepalm...
My definitive conclusion is : there's a bug in EF 6 implementation. It should toggle the
CommandBehavior
toSequentialAccess
when an async call is made over a table containing abinary(max)
column. The problem of creating too many Task, slowing down the process, is on the Ado.Net side. The EF problem is that it doesn't use Ado.Net as it should.Now you know instead of using the EF6 async methods, you would better have to call EF in a regular non-async way, and then use a
TaskCompletionSource<T>
to return the result in an async way.Note 1 : I edited my post because of a shameful error.... I've done my first test over the network, not locally, and the limited bandwidth have distorted the results. Here are the updated results.
Note 2 : I didn't extends my test to other uses cases (ex :
nvarchar(max)
with a lot of data), but there are chances the same behavior happens.Note 3 : Something usual for the
ToList()
case, is the 12% CPU (1/8 of my CPU = 1 logical core). Something unusual is the maximum 20% for theToListAsync()
case, as if the Scheduler could not use all the Treads. It's probably due to the too many Task created, or maybe a bottleneck in TDS parser, I don't know...
相关问答
更多-
渲染库不是简单地“阻塞线程”,它正在执行渲染的工作。 没有什么比你能做的更好的了。 The rendering library is not simply "blocking a thread", it is doing work performing your rendering. There is nothing better you can do.
-
为什么使用Hibernate进行查询缓存使查询速度降低了十倍?(Why does query caching with Hibernate make the query ten times slower?)[2021-06-17]
查询缓存的工作方式是只缓存查询返回的对象的ID 。 因此,您的初始SELECT语句可能会返回所有对象,Hibernate会将它们还给您并记住ID。 然而,当你下一次发出查询时,Hibernate会遍历ID列表,并意识到它需要实现实际数据。 所以它返回到数据库来休息。 它每行都有一个SELECT,这正是你所看到的。 现在,在你认为“这个特性显然被破坏了”之前,它这样工作的原因是查询缓存被设计为与二级缓存协同工作。 如果这些对象在第一次查询后存储在二级缓存中,那么Hibernate将会在那里查找,以满足每个ID ... -
实体框架异步操作需要十倍的时间才能完成(Entity Framework async operation takes ten times as long to complete)[2023-09-17]
我发现这个问题非常有趣,特别是因为我在Ado.Net和EF 6上使用async 。我希望有人给出这个问题的解释,但并没有发生。 所以我试图重现这个问题在我身边。 我希望你们中有人会发现这个有趣的。 第一个好消息:我转载它:)和差异是巨大的。 有一个因子8 ... 首先,我怀疑与CommandBehavior有关的事情,因为我读了一篇有关与Ado async 的有趣的文章 ,他说: “由于非顺序访问模式必须存储整行的数据,如果从服务器读取大列(如varbinary(MAX),varchar(MAX),nvar ... -
是。 我刚刚通过分析控制器功能找到了问题。 因此,根据Codeigniter分析结果如下, 控制器执行时间:27.7074秒。 然后我看了Chrome控制台完成请求的时间,从那我开始知道一个问题。 Stalled : 17.32 s DNS Lookup: 1.000 ms Initial connection :262.000 ms Request/Response TIME Request sent 0 Waiting (TTFB) 1.03 s Content Download ...
-
您必须具体化列表,然后才能对该DbContext执行其他操作,因为在枚举IQueryable时正在使用DbContext 。 因此,添加.ToList()可以解决问题,因为在迭代开始之前会从DbContext检索所有实体。 foreach (Entities.Apointment app in apps.ToList()) // added ToList() 如果您想使用async实现列表,则可以执行此操作 foreach (Entities.Apointment app in (await apps.T ...
-
在异步操作中锁定对象(locking object in async operation)[2022-06-16]
如何将委托更改为如下所示: var cacheValue = operation(); lock (this.Cache) { if (!this.Cache.Contains(key)) { // Perform the operation to get value for cache here this.Cache.Add(key, c ... -
WPF单元测试框架(WPF Unit testing framework)[2022-09-17]
您可以尝试使用NUnit,就像您将使用任何其他组件一样。 你也可以尝试MVC。 查看MVCtoUnitTestinWPF的CodeProject 希望它服务。 You can try with NUnit, just like you'll do with any other component. Optionally you can try MVC. See CodeProject for MVCtoUnitTestinWPF Hope it serves. -
你可以尝试.ToListAsync或: public static Task
- > GetAllPostCodeRegions(string country)
{
return Task.Run(() =>
{
using (var db = new PlaceDBContext())
{
return db.PostCodes
.Where(p ...
-
您可以使用await关键字获取异步任务的值 http://msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx you can use the await keyword for getting values of async task http://msdn.microsoft.com/en-us/library/vstudio/hh156528.aspx
-
将嵌套分组展开为 ^-{2,}\s*(\w+(?:\s+\w+)*)\s*-{2,}$ ^^^^^^^^^^^ 否则,您的模式将容易发生灾难性的回溯 。 请参阅正则表达式演示 或者,使用原子组禁用任何回溯到交替组: ^-{2,}\s*((?>\w+(?:\w+|\s)*))\s*-{2,}$ ^^^ ^ 看到这个正则表达式演示 通常,避免在较长的模式中使用嵌套量词(如(\w+|\s)* )中的替换。 Unroll the nes ...