循环遍历双引号但忽略单引号中的双引号
Posted
技术标签:
【中文标题】循环遍历双引号但忽略单引号中的双引号【英文标题】:Loop through double quotes but ignore double quotes in single quotes 【发布时间】:2013-10-31 23:29:56 【问题描述】:我猜是逻辑问题。我正在使用 C# 进行编码,但欢迎使用通用伪代码解决方案。
我有这个文本文件,例如,这个文本:
blah "hello john"
blah 'the code is "flower" '
blah "good night"
我想遍历双引号并对它们做一些事情,但我想忽略单引号中包含的双引号。我得到了开始双引号和结束双引号的位置(string data
包含文本文件的内容):
C#
// Start searching from beginning
int quotestart = 0, quoteend = 0;
while (data.IndexOf('"', quotestart) != -1)
// Get opening double quote
quotestart = data.IndexOf('"', quotestart);
// Get ending double quote
quoteend = data.IndexOf('"', quotestart + 1);
string sub = data.Substring(quotestart + 1, quoteend - quotestart - 1);
Console.WriteLine(sub);
// Set the start position for the next round
quotestart = quoteend + 1;
使用我的代码,输出将是:
hello john
flower
good night
因为“花”在单引号内,我希望我的输出是:
hello john
good night
编辑
我目前正在研究一种方法,首先用“A”填充单引号之间的所有数据。这样,当我遍历双引号时,单引号之间的任何数据都会被忽略。不确定这是否是正确的方法。
【问题讨论】:
这听起来像是有限状态机的工作。 我尝试在谷歌上搜索有限状态机,但没有接受过计算机工程方面的正式培训,我必须承认我有点迷茫。你有什么额外的建议吗? 问题未明确。1 " 2 ' 3 " 4 " 5 ' 6 " 7
呢。您想忽略 1 因为它在引号之外。你想产生2 ' 3
然后忽略4,产生5 ' 6
,还是你想说单引号内的所有东西都被忽略了,所以这产生2 6
?
根据具体情况,这可能是下推自动机的工作,而不是有限自动机。
Eric,你提出的第一个解决方案就是我要找的。span>
【参考方案1】:
我尝试在谷歌上搜索有限状态机,但没有接受过正式的计算机工程培训,我必须承认我有点迷茫。你有什么补充的指点吗?
FSM 是最简单的计算机形式之一。这个想法是您拥有有限数量的“状态”信息和稳定的输入流。每个输入都会导致状态以可预测的方式发生变化,仅基于当前状态和当前输入,并导致可预测的输出发生。
因此,假设您的输入是单个字符,而您的输出是单个字符或“空”字符。这是一个可以满足您需求的 FSM:
状态为OUTSIDE
、INSIDEDOUBLE
和INSIDESINGLE
。
输入为"
、'
和x
。 (WOLOG 让x
代表任何其他角色。)
我们有三个状态和三个输入,所以有九种可能的组合。
如果我们是OUTSIDE
并得到x
,则保持OUTSIDE
并发出null
。
如果我们是OUTSIDE
并得到"
,则转到INSIDEDOUBLE
并发出null
。
如果我们是OUTSIDE
并得到'
,则转到INSIDESINGLE
并发出null
。
如果我们是INSIDEDOUBLE
并得到x
,则留在INSIDEDOUBLE
并发出x
如果我们是INSIDEDOUBLE
并得到"
,则转到OUTSIDE
并发出null
如果我们是INSIDEDOUBLE
并得到'
,则留在INSIDEDOUBLE
并发出'
如果我们是INSIDESINGLE
并得到x
,则保持INSIDESINGLE
并发出null
如果我们是INSIDESINGLE
并得到"
,则留在INSIDESINGLE
并发出null
如果我们是INSIDESINGLE
并得到'
,则转到OUTSIDE
并发出null
唯一剩下的就是说开始状态是OUTSIDE
。
假设输入是1 " 2 " 3 ' 4 " 5 " ' 6
。状态和输出是:
OUTSIDE
得到1
,发出null
,保持OUTSIDE
。
OUTSIDE
得到"
,发出null
,转到INSIDEDOUBLE
。
INSIDEDOUBLE
得到2
,发出2
,保持INSIDEDOUBLE
INSIDEDOUBLE
得到"
,发出null
,转到OUTSIDE
。
OUTSIDE
得到3
,发出null
,保持OUTSIDE
。
OUTSIDE
得到'
,发出null
,去INSIDESINGLE
...其余的自己填写。
你写代码的草图就够了吗?
【讨论】:
谢谢,我明白了。回家后试试这个! "如果我们是 INSIDEDOUBLE 并得到 ",则转到 OUTSIDEDOUBLE 并发出 null" 我们不应该转到 OUTSIDE 吗?应该只有 3 个状态不?对于 INSIDESINGLE 和 get 'go OUTSIDESINGLE 也是如此? 【参考方案2】:很好的解决方案;对于小型 FSM,使用 switch 语句是执行此操作的传统方法,但是当状态和输入的数量变得庞大而复杂时,它就会变得笨拙。这是一个更易于扩展的替代解决方案:表格驱动解决方案。也就是把关于transitions和actions的事实放到一个数组中,然后FSM无非就是一系列数组查找:
// States
const int Outside = 0;
const int InDouble = 1;
const int InSingle = 2;
// Inputs
const int Other = 0;
const int DoubleQuote = 1;
const int SingleQuote = 2;
static readonly int[,] stateTransitions =
/* Other DoubleQ SingleQ */
/* Outside */ Outside, InDouble, InSingle ,
/* InDouble */ InDouble, Outside, InDouble ,
/* InSingle */ InSingle, InSingle, Outside
;
// Do we emit the character or ignore it?
static readonly bool[,] actions =
/* Other DoubleQ SingleQ */
/* Outside */ false, false, false ,
/* InDouble */ true, false, true ,
/* InSingle */ false, false, false
;
static int Classify(char c)
switch (c)
case '\'': return SingleQuote;
case '\"': return DoubleQuote;
default: return Other;
static IEnumerable<char> FSM(IEnumerable<char> inputs)
int state = Outside;
foreach (char input in inputs)
int kind = Classify(input);
if (actions[state, kind])
yield return input;
state = stateTransitions[state, kind];
现在我们可以得到结果
string.Join("", FSM(@"1""2'3""4""5'6""7'8""9""A'B"))
【讨论】:
我喜欢这个!用户有一个问题,Eric 提供了伪代码,并且对于更改,用户实际上能够生成一个有效的 C# 代码。作为蛋糕上的一颗樱桃,Eric 提供了一个更好的实现。我希望关于 SO 的每个问题/答案都是这样的。 @SolutionYogi:谢谢——虽然我不会说“更好”,我只会说“不同”。正如 perl 程序员所说,TIMTOWTDIBSCINABTE。 你太谦虚了。 :) 顺便说一句,我还使用 FSM 库发布了一个答案。这是更多的代码,但有助于更好地记录 FSM,将不胜感激您的 cmets。 Eric,如果您有几分钟的时间,能否请您看看我对这个代码审查问题的回答:codereview.stackexchange.com/questions/33152/… 原始问题中的代码正是我 5 年前写的。但是由于您的回答/博客文章,我已经成为一个更好的开发人员(恕我直言),如果您有任何额外的 cmets,我将不胜感激。 @SolutionYogi:对我来说看起来很合理!【参考方案3】:非常感谢 Eric Lippert 提供了此解决方案背后的逻辑。我在下面提供我的 C# 解决方案,以防有人需要。为了清楚起见,我留下了一些不必要的重新分配。
string state = "outside";
for (int i = 0; i < data.Length; i++)
c = data[i];
switch (state)
case "outside":
switch (c)
case '\'':
state = "insidesingle";
break;
case '"':
state = "insidedouble";
break;
default:
state = "outside";
break;
break;
case "insidedouble":
switch (c)
case '\'':
state = "insidedouble";
Console.Write(c);
break;
case '"':
state = "outside";
break;
default:
state = "insidedouble";
Console.Write(c);
break;
break;
case "insidesingle":
switch (c)
case '\'':
state = "outside";
break;
case '"':
state = "insidesingle";
break;
default:
state = "insidesingle";
break;
break;
【讨论】:
对于您的状态变量,枚举将是比字符串更好的选择。可能还有其他方法可以改进您的代码,但 codereview.SE 是寻求此类建议的地方。 谢谢。实际上,我自己使用了一个 int ,只是放了一个字符串,以使其对其他人更清楚。但你是对的,我应该使用枚举!【参考方案4】:只是为了好玩,我决定使用一个名为stateless 的非常轻量级的 FSM 库来解决这个问题。
如果您要使用此库,代码将如下所示。
就像 Eric 的解决方案一样,可以轻松更改以下代码以满足新的需求。
void Main()
Console.WriteLine(string.Join("", GetCharacters(@"1""2'3""4""5'6""7'8""9""A'B")));
public enum CharacterType
Other,
SingleQuote,
DoubleQuote
public enum State
OutsideQuote,
InsideSingleQuote,
InsideDoubleQuote
public IEnumerable<char> GetCharacters(string input)
//Initial state of the machine is OutSideQuote.
var sm = new StateMachine<State, CharacterType>(State.OutsideQuote);
//Below, we configure state transitions.
//For a given state, we configure how CharacterType
//transitions state machine to a new state.
sm.Configure(State.OutsideQuote)
.Ignore(CharacterType.Other)
//If you are outside quote and you receive a double quote,
//state transitions to InsideDoubleQuote.
.Permit(CharacterType.DoubleQuote, State.InsideDoubleQuote)
//If you are outside quote and you receive a single quote,
//state transitions to InsideSingleQuote.
//Same logic applies for other state transitions below.
.Permit(CharacterType.SingleQuote, State.InsideSingleQuote);
sm.Configure(State.InsideDoubleQuote)
.Ignore(CharacterType.Other)
.Ignore(CharacterType.SingleQuote)
.Permit(CharacterType.DoubleQuote, State.OutsideQuote);
sm.Configure(State.InsideSingleQuote)
.Ignore(CharacterType.Other)
.Ignore(CharacterType.DoubleQuote)
.Permit(CharacterType.SingleQuote, State.OutsideQuote);
foreach (var character in input)
var characterType = GetCharacterType(character);
sm.Fire(characterType);
if(sm.IsInState(State.InsideDoubleQuote) && characterType != CharacterType.DoubleQuote)
yield return character;
public CharacterType GetCharacterType(char input)
switch (input)
case '\'': return CharacterType.SingleQuote;
case '\"': return CharacterType.DoubleQuote;
default: return CharacterType.Other;
【讨论】:
这是一个非常流畅的界面。我应该看看那个图书馆。 @EricLippert 很高兴您喜欢它。对于更复杂的需求,我肯定会推荐Solid State:code.google.com/p/solid-state它的灵感来自于无状态库。以上是关于循环遍历双引号但忽略单引号中的双引号的主要内容,如果未能解决你的问题,请参考以下文章