Checking source files (text files) for a certain structure in C# - c#

Below I have an example of some text (in this case it's C source code, could be any structured text honestly). I am trying to read a couple files, with variable length and different structures and figure out if for example, after every #define an #include "test.h" is present.
The same case can apply to both, within and out of the preprocessor directives (#if, #ifdef, #ifndef, #endif). It is also allowed that a #include "test.h" appears after a #endif, if the proper #define was within the if-directive prior to this.
A few side notes:
I extract the text from the file, do some actions such as removing comments, irrelevant lines, empty lines and soon and return the file as a string[]. This makes it fairly easy to iterate and jump back and forth between the lines SourceCode[i]
I have attempted to implement a solution with a few different approaches, and never managed to fully hit the nail.
My first attempt consisted of an endless amount of if-else-statements and while-loops, for every imaginable path in the logic. This ended up being so hard to maintain, confusing and to hard keep control upon.
Next, I've tried implementing a state machine, to keep track of where I am in the text file, jumping to different states as needed. I couldn't find a perfect solution.
Another attempt I've tried was using a stack, pushing a #define to the stack, checking what it was, pushing the next line to the stack, checking if it is #include, if not then return an error. This also, of course, is a bit more complicated as soon as I have directives, as one #include is sufficient for multiple #defines.
I've looked into parsers (mainly Antlr), realizing that this is possibly way too overkill for such a problem, also considering I have absolutely no clue about parsers and would need to make my own grammar.
Source code example
// directives
#if (TEST == true)
#define START_TEST_1
#include "test.h"
#else
#define START_TEST_2
#include "test.h"
#endif
#if (TEST == true)
#define STOP_TEST_1
#else
#define STOP_TEST_2
#endif
#include "test.h"
// no directives
#define START_TEST_3
#include "test.h"
#define STOP_TEST_3
#include "test.h"
Does anyone have some general tips and can maybe point me in a specific direction. What would be a suitable solution to this problem?
Edit: #jdweng
dt.Columns.Add("Next #elif State", typeof(int));
dt.Rows.Add(new object[] { 12, DEFINE_STATE.FOUND_ELIF, 13, 0, 2, 7, 12, 10, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 13, DEFINE_STATE.FOUND_DEFINE_IN_ELIF, -1, 14, 2, 7, 12, 10, ERROR.DEFINE_FOLLOWED_BY_DEFINE, ACTION.SET_DEFINE_ELIF_LINE_NUMBER });
dt.Rows.Add(new object[] { 14, DEFINE_STATE.FOUND_INCLUDE_IN_ELIF, 13, 0, 2, 7, 12, 10, ERROR.NO_ERROR, ACTION.RESET_DEFINE_ELIF_LINE_NUMBER });
I added a check to see if elif_level is == 0, if so, then proceed as usual, removing the level of if-nest. Otherwise I remove elif_level and then the if-nest level.
Do the above rows look correct? I am thinking of either adding a bool variable to states that is set to true if elif is found, and later when I find an #endif I can pop all states that have elif set to true.

I've been parsing text files like this for over 40 years. This is a complicate logic issue so with any complicated logic issue I would use a State Machine. First I drew a state diagram
Then I wrote code to implement the state table
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Data;
namespace ConsoleApplication1
{
public enum DEFINE_STATE
{
SPECIAL = -4, //define followed by IF
NONE = -3,
INVALID = -2, //Compile will give error, cannot occur
ERROR = -1,
DO_NOT_CARE = 0,
START = 1,
FOUND_IF = 2,
FOUND_DEFINE_IN_IF = 3,
FOUND_DEFINE_NOT_IN_IF = 4,
FOUND_INCLUDE_IN_IF = 5,
FOUND_ELSE = 6,
FOUND_DEFINE_IN_ELSE = 7,
FOUND_INCLUDE_IN_ELSE = 8,
FOUND_INCLUDE_NOT_IN_IF = 9,
FOUND_END_IF = 10,
RETURN = 11,
FOUND_ELIF = 12,
FOUND_DEFINE_IN_ELIF = 13,
FOUND_INCLUDE_IN_ELIF = 14,
}
public enum ERROR
{
NO_ERROR,
DEFINE_FOLLOWED_BY_DEFINE,
DEFINE_FOLLOWED_BY_DEFINE_OR_IF
}
public enum TABLE_COLUMN
{
STATE = 0,
DESCRIPTION = 1,
DEFINE,
INCLUDE,
IF,
ELSE,
ELIF,
END_IF,
ERROR,
ACTION
}
public enum ACTION
{
NONE,
RESET_DEFINE_LINE_NUMBER,
RESET_DEFINE_IF_LINE_NUMBER,
RESET_DEFINE_ELSE_LINE_NUMBER,
RESET_DEFINE_ELIF_LINE_NUMBER,
SET_DEFINE_LINE_NUMBER,
SET_DEFINE_IF_LINE_NUMBER,
SET_DEFINE_ELSE_LINE_NUMBER,
SET_DEFINE_ELIF_LINE_NUMBER,
}
public class State_Variables
{
public int define_Line_Number = 0;
public int define_If_Line_Number = 0;
public int define_Else_Line_Number = 0;
public int define_Elif_Line_Number = 0;
public int return_end_level = 0;
public DEFINE_STATE state = DEFINE_STATE.START;
public DataRow row { get; set; }
}
class Program
{
const string FILENAME = #"c:\temp\test.txt";
static void Main(string[] args)
{
string pattern = "#include\\s+\"test.h\"";
StreamReader reader = new StreamReader(FILENAME);
string input = "";
DataTable dt = new DataTable();
dt.Columns.Add("State", typeof(int));
dt.Columns.Add("Description", typeof(DEFINE_STATE));
dt.Columns.Add("Next Define State", typeof(int));
dt.Columns.Add("Next Include State", typeof(int));
dt.Columns.Add("Next IF State", typeof(int));
dt.Columns.Add("Next Else State", typeof(int));
dt.Columns.Add("Next ELIF State", typeof(int));
dt.Columns.Add("Next ENDIF State", typeof(int));
dt.Columns.Add("Error Number", typeof(ERROR));
dt.Columns.Add("Action", typeof(ACTION));
//0 do not care
//-1 error
//-2 invalid
dt.Rows.Add(new object[] { 1, DEFINE_STATE.START, 4, 0, 2, -2, -2, -2, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 2, DEFINE_STATE.FOUND_IF, 3, 0, 2, 6, 12, 10, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 3, DEFINE_STATE.FOUND_DEFINE_IN_IF, -1, 5, 2, 6, 12, 10, ERROR.DEFINE_FOLLOWED_BY_DEFINE, ACTION.SET_DEFINE_IF_LINE_NUMBER });
dt.Rows.Add(new object[] { 4, DEFINE_STATE.FOUND_DEFINE_NOT_IN_IF, -1, 9, -4, -2, -2, -2, ERROR.DEFINE_FOLLOWED_BY_DEFINE, ACTION.SET_DEFINE_LINE_NUMBER });
dt.Rows.Add(new object[] { 5, DEFINE_STATE.FOUND_INCLUDE_IN_IF, 3, 0, 2, 6, 12, 10, ERROR.NO_ERROR, ACTION.RESET_DEFINE_IF_LINE_NUMBER });
dt.Rows.Add(new object[] { 6, DEFINE_STATE.FOUND_ELSE, 7, 0, 2, -2, -2, 10, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 7, DEFINE_STATE.FOUND_DEFINE_IN_ELSE, -1, 8, 2, -2, -2, 10, ERROR.DEFINE_FOLLOWED_BY_DEFINE, ACTION.SET_DEFINE_ELSE_LINE_NUMBER });
dt.Rows.Add(new object[] { 8, DEFINE_STATE.FOUND_INCLUDE_IN_ELSE, 7, 0, 2, -2, -2, 10, ERROR.NO_ERROR, ACTION.RESET_DEFINE_ELSE_LINE_NUMBER });
dt.Rows.Add(new object[] { 9, DEFINE_STATE.FOUND_INCLUDE_NOT_IN_IF, 4, 0, 2, -2, -2, -2, ERROR.NO_ERROR, ACTION.RESET_DEFINE_LINE_NUMBER });
dt.Rows.Add(new object[] { 10, DEFINE_STATE.FOUND_END_IF, 11, 1, 2, -2, -2, -2, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 11, DEFINE_STATE.RETURN, -2, -2, 2, -2, -2, -2, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 12, DEFINE_STATE.FOUND_ELIF, 13, 0, 2, -2, 12, 10, ERROR.NO_ERROR, ACTION.NONE });
dt.Rows.Add(new object[] { 13, DEFINE_STATE.FOUND_DEFINE_IN_ELIF, -1, 14, 2, -2, 12, 10, ERROR.DEFINE_FOLLOWED_BY_DEFINE, ACTION.SET_DEFINE_ELIF_LINE_NUMBER });
dt.Rows.Add(new object[] { 14, DEFINE_STATE.FOUND_INCLUDE_IN_ELIF, 13, 0, 2, 7, 12, 10, ERROR.NO_ERROR, ACTION.RESET_DEFINE_ELIF_LINE_NUMBER });
int level = 0;
List<State_Variables> states = new List<State_Variables>();
State_Variables newState = new State_Variables();
states.Add(newState);
DEFINE_STATE nextState = DEFINE_STATE.START;
ACTION action = ACTION.NONE;
int line_number = 0;
while ((input = reader.ReadLine()) != null)
{
line_number++;
input = input.Trim();
if (input.StartsWith("//")) continue; //ignore comments
if (input.Length == 0) continue;
Boolean returnFromIF = false;
Match match = Regex.Match(input, pattern);
//test if end if is followed by include
if (states[level].state == DEFINE_STATE.FOUND_END_IF)
{
int return_end_level = 0;
do
{
if (!match.Success)
{
int define_If_Line_Number = states[level].define_If_Line_Number;
int define_Else_Line_Number = states[level].define_Else_Line_Number;
int define_Elif_Line_Number = states[level].define_Elif_Line_Number;
if (define_If_Line_Number != 0)
{
Console.WriteLine("Define in IF at line {0} does not have and include", define_If_Line_Number.ToString());
}
if (define_Else_Line_Number != 0)
{
Console.WriteLine("Define in ELSE at line {0} does not have and include", define_Else_Line_Number.ToString());
}
if (define_Elif_Line_Number != 0)
{
Console.WriteLine("Define in ELSE at line {0} does not have and include", define_Else_Line_Number.ToString());
}
}
return_end_level = states[level].return_end_level;
states.RemoveAt(level--);
} while (level > return_end_level);
returnFromIF = true;
}
else
{
states[level].row = dt.AsEnumerable().Where(x => x.Field<int>((int)TABLE_COLUMN.STATE) == (int)states[level].state).FirstOrDefault();
}
nextState = DEFINE_STATE.NONE;
//check if defines are terminated with include
if (input.Contains("#define"))
{
nextState = (DEFINE_STATE)states[level].row.Field<int>((int)TABLE_COLUMN.DEFINE);
}
if (match.Success)
{
if (returnFromIF)
{
nextState = states[level].state;
}
else
{
nextState = (DEFINE_STATE)states[level].row.Field<int>((int)TABLE_COLUMN.INCLUDE);
}
}
if (input.Contains("#if"))
{
nextState = (DEFINE_STATE)states[level].row.Field<int>((int)TABLE_COLUMN.IF);
states.Add(new State_Variables());
level++;
states[level].return_end_level = level - 1;
}
if (input.Contains("#else"))
{
nextState = (DEFINE_STATE)states[level].row.Field<int>((int)TABLE_COLUMN.ELSE);
}
if (input.Contains("#elif"))
{
nextState = (DEFINE_STATE)states[level].row.Field<int>((int)TABLE_COLUMN.ELIF);
states.Add(new State_Variables());
level++;
states[level].return_end_level = states[level - 1].return_end_level;
}
if (input.Contains("#endif"))
{
nextState = (DEFINE_STATE)states[level].row.Field<int>((int)TABLE_COLUMN.END_IF);
}
if ((nextState != DEFINE_STATE.ERROR) && (nextState != DEFINE_STATE.DO_NOT_CARE) && (nextState != DEFINE_STATE.NONE))
{
states[level].state = nextState;
}
switch (nextState)
{
case DEFINE_STATE.DO_NOT_CARE:
//stay at current state
break;
case DEFINE_STATE.NONE: //stay at current state
Console.WriteLine("Did not find state at line {0}", line_number);
break;
case DEFINE_STATE.INVALID:
Console.WriteLine("Invalid IF/ELSE/END_IF at line {0}", line_number);
break;
case DEFINE_STATE.ERROR:
action = states[level].row.Field<ACTION>((int)TABLE_COLUMN.ACTION);
switch (action)
{
case ACTION.SET_DEFINE_LINE_NUMBER:
Console.WriteLine("Define followed by Define at line {0}", states[level].define_Line_Number.ToString());
states[level].define_Line_Number = line_number;
break;
case ACTION.SET_DEFINE_IF_LINE_NUMBER:
Console.WriteLine("Define in IF followed by Define by at line {0}", states[level].define_If_Line_Number.ToString());
states[level].define_If_Line_Number = line_number;
break;
case ACTION.SET_DEFINE_ELSE_LINE_NUMBER:
Console.WriteLine("Define in ELSE followed by Define at line {0}", states[level].define_Else_Line_Number.ToString());
states[level].define_Else_Line_Number = line_number;
break;
case ACTION.SET_DEFINE_ELIF_LINE_NUMBER :
Console.WriteLine("Define in ELIF followed by Define at line {0}", states[level].define_Elif_Line_Number.ToString());
states[level].define_Elif_Line_Number = line_number;
break;
}
break;
case DEFINE_STATE.SPECIAL:
Console.WriteLine("Define followed IF at line {0}", states[level].define_Line_Number.ToString());
states[level - 1].state = DEFINE_STATE.START;
states[level].state = DEFINE_STATE.FOUND_IF;
nextState = DEFINE_STATE.FOUND_IF;
states[level].row = dt.AsEnumerable().Where(x => x.Field<DEFINE_STATE>((int)TABLE_COLUMN.STATE) == nextState).FirstOrDefault();
break;
default:
states[level].row = dt.AsEnumerable().Where(x => x.Field<DEFINE_STATE>((int)TABLE_COLUMN.STATE) == nextState).FirstOrDefault();
action = states[level].row.Field<ACTION>((int)TABLE_COLUMN.ACTION);
switch (action)
{
case ACTION.RESET_DEFINE_LINE_NUMBER:
states[level].define_Line_Number = 0;
break;
case ACTION.RESET_DEFINE_IF_LINE_NUMBER:
states[level].define_If_Line_Number = 0;
break;
case ACTION.RESET_DEFINE_ELSE_LINE_NUMBER:
states[level].define_Else_Line_Number = 0;
break;
case ACTION.RESET_DEFINE_ELIF_LINE_NUMBER:
states[level].define_Elif_Line_Number = 0;
break;
case ACTION.SET_DEFINE_LINE_NUMBER:
states[level].define_Line_Number = line_number;
break;
case ACTION.SET_DEFINE_IF_LINE_NUMBER:
states[level].define_If_Line_Number = line_number;
break;
case ACTION.SET_DEFINE_ELSE_LINE_NUMBER:
states[level].define_Else_Line_Number = line_number;
break;
case ACTION.SET_DEFINE_ELIF_LINE_NUMBER:
states[level].define_Elif_Line_Number = line_number;
break;
}
states[level].state = nextState;
break;
}
}
//final checks
int define_Line_Number = states[level].define_Line_Number;
if (define_Line_Number != 0)
{
Console.WriteLine("Define at line {0} does not have and include", define_Line_Number.ToString());
}
if (level != 0)
{
Console.WriteLine("Did not close all IFs with End_If");
}
Console.WriteLine("Done");
Console.ReadLine();
}
}
}

If the common denominator of all text you want to analyze is a hierarchically structured document, maybe you should start by converting it to that and then do the rest of the analysis on the parsed document and not do both at the same time. Perhaps converting it to an XML-document could be sufficient for your case and then do the analysis using XSLT/XPath (Or LINQ for XDocument if you prefer that). This is basically how other code analysis is performed as well (Roslyn Code Analyzers with Syntax Trees except much more fancy of course).

Related

Google Or-Tools Routing - Solution is null

I am trying to implement VRPTW of Google OR-Tools. But I am facing an issue. When I pass the dynamic Time matrix then the solution object is null but when I pass the Time matrix which is given in example then it worked.
Here is my code
public class DataModel
{
public long[,] DistanceMatrix { get; set; }
public long[,] TimeMatrix = {
//commented matrix is dynamic generated
// {0,5,20,10,0,5},
//{5,0,25,10,5,5},
//{20,25,0,30,20,20},
//{10,10,30,0,10,15},
//{0,5,20,10,0,5},
//{5,5,20,15,5,0},
{0, 6, 9, 8, 7, 3},
{6, 0, 8, 3, 2, 6},
{9, 8, 0, 11, 10, 6},
{8, 3, 11, 0, 1, 7},
{7, 2, 10, 1, 0, 6},
{3, 6, 6, 7, 6, 0},
};
public long[,] TimeWindows = {
{0, 5}, // depot
{7, 12}, // 1
{10, 15}, // 2
{16, 18}, // 3
{10, 13}, // 4
{0, 5}, // 5
};
public int VehicleNumber = 3;
public int Depot = 0;
};
Here is the main function code
DataModel data = new DataModel();
// data.TimeMatrix = TimeMatrix;
// Create Routing Index Manager
RoutingIndexManager manager = new RoutingIndexManager(
data.TimeMatrix.GetLength(0),
data.VehicleNumber,
data.Depot);
// Create a Routing Model.
RoutingModel routing = new RoutingModel(manager);
// Create and register a transit callback.
int transitCallbackIndex = routing.RegisterTransitCallback(
(long fromIndex, long toIndex) =>
{
// Convert from routing variable Index to distance matrix NodeIndex.
var fromNode = manager.IndexToNode(fromIndex);
var toNode = manager.IndexToNode(toIndex);
return data.TimeMatrix[fromNode, toNode];
}
);
// Define the cost of each arc.
routing.SetArcCostEvaluatorOfAllVehicles(transitCallbackIndex);
// Add Distance constraint.
routing.AddDimension(
transitCallbackIndex, // transit callback
30, // allow waiting time
30, // vehicle maximum capacities
false, // start cumul to zero
"Time");
RoutingDimension timeDimension = routing.GetMutableDimension("Time");
// Add time window constraints for each location except depot.
for (int i = 1; i < data.TimeWindows.GetLength(0); ++i)
{
long index = manager.NodeToIndex(i);
timeDimension.CumulVar(index).SetRange(
data.TimeWindows[i, 0],
data.TimeWindows[i, 1]);
}
// Add time window constraints for each vehicle start node.
for (int i = 0; i < data.VehicleNumber; ++i)
{
long index = routing.Start(i);
timeDimension.CumulVar(index).SetRange(
data.TimeWindows[0, 0],
data.TimeWindows[0, 1]);
}
// Instantiate route start and end times to produce feasible times.
for (int i = 0; i < data.VehicleNumber; ++i)
{
routing.AddVariableMinimizedByFinalizer(
timeDimension.CumulVar(routing.Start(i)));
routing.AddVariableMinimizedByFinalizer(
timeDimension.CumulVar(routing.End(i)));
}
// Setting first solution heuristic.
RoutingSearchParameters searchParameters =
operations_research_constraint_solver.DefaultRoutingSearchParameters();
searchParameters.FirstSolutionStrategy =
FirstSolutionStrategy.Types.Value.PathCheapestArc;
// Solve the problem.
Assignment solution = routing.SolveWithParameters(searchParameters); //it is null when dynamic time matrix is used, but it is not null when time matrix mentioned in example is used.
The problem seems to be in AddDimension Method. I am struck in this but could not find any solution. Please suggest any solution.
Solution is null indicates that the solver was not able to find any feasible solution. Most likely your time windows are too tight.
Either try relaxing your time windows
or make sure nodes are optional (using addDisjunction).

Trouble converting C++ method to C#

I'm trying to translate a C++ method that uses a homegrown form of encryption. But I don't understand what Buffer[x] and Input[x] are doing? As a C# developer (beginner at that), It looks like they should be arrays but they aren't declared as such. Can anyone explain please?
The input string "{x;ƒ~sq{j|tLtuq" translates to "MY SOFTWARE INC"
AnsiString __fastcall TMyMain::Decode(AnsiString Input)
{
int error[] = { 2, 9, 5, 4, 1, 6, 7, 12, 19, 3, 1, 0, 21, 17 };
int x;
AnsiString Buffer = Input;
if (encoded!=0)
{
int count = 0;
for(x=Input.Length();x>=1;x--)
{
Buffer[x] = Input[x]-48+error[count];
count++;
if (count>=14)
count=0;
}
}
return Buffer;
}
Here's how I'd translate it. Note I have no idea where encoded comes from, so I left that out. The trick is to use a StringBuilder for your buffer so you can mutate the characters, another options would be a char array.
public static string Decode(string input)
{
int[] error = { 2, 9, 5, 4, 1, 6, 7, 12, 19, 3, 1, 0, 21, 17 };
StringBuilder buffer = new StringBuilder(input);
int count = 0;
for (int x = input.Length - 1; x >= 0; x--) {
buffer[x] = (char)(input[x] - 48 + error[count]);
count++;
if (count >= 14)
count = 0;
}
return buffer.ToString();
}
This however outputs "MY TOFTWARE INC" for the input "{x;ƒ~sq{j|tLtuq", so I'm not exactly sure if the issue is with your string or the code.
I've come up with this, but it doesn't quite return the expected results. Are you sure of the input string?
Input and output is string.
var encoded = true;
var input = "{x;ƒ~sq{j|tLtuq";
var output = Decode(input);
Console.WriteLine($"input \"{input}\", output \"{output}\"");
private static string Decode(string input)
{
int[] error = { 2, 9, 5, 4, 1, 6, 7, 12, 19, 3, 1, 0, 21, 17 };
var buffer = new char[input.Length];
if (encoded)
{
int count = 0;
for(var x=input.Length-1;x>=0;x--)
{
buffer[x] = (char) ((Convert.ToInt16(input[x])-48 + error[count]) & 0xFF);
count++;
if (count>=error.Length)
count=0;
}
return new string(buffer)
}
return input;
}
Calling this writes the following to the console:
input "{x;ƒ~sq{j|tLtuq", output "MY bOFTWARE INC"
I don't know if it helps but after some reverse engeneering you could use the following errors array (with the StringBuilder implementation) to dispaly the proper information.
int[] error = { 2, 9, 5, 4, 1, 6, 7, 12, 19, 3, 1, -6, 21, 17 };//MY SOFTWARE INC
It has to do with how characters were encoded in c++ vs c# so you could try to play around with System.Text.Encoding.

Creating a new file from existing file

I have a text file that looks like this when I open it using a streamreader: (The "Remove" is just to show you what I want to do)
A, 1, 2, 3, 4, 5
B, 1, 2, 2, 2, 2
B, 1, 1, 1, 1, 1
A, 2, 2, 3, 4, 5 -- Remove
A, 1, 2, 3, 4, 5
A, 2, 2, 3, 4, 5 -- Remove
B, 1, 2, 2, 2, 2 -- Remove
B, 1, 1, 1, 1, 1 -- Remove
A, 1, 2, 3, 4, 5
B, 1, 2, 2, 2, 2
The "A"s are the parent rows and the "B"s are children rows directly below parent rows. Some A's may not have children rows. Basically, I want a new text file that only has the A's and their children (B's) where the second field in the A's row does not include a 2. So my new text file (using streamwriter) would look like:
A, 1, 2, 3, 4, 5
B, 1, 2, 2, 2, 2
B, 1, 1, 1, 1, 1
A, 1, 2, 3, 4, 5
A, 1, 2, 3, 4, 5
B, 1, 2, 2, 2, 2
I can get the lines A, without "2", ... but having a hard time getting it's children lines below it...
Help anyone?
I think I got it working, but it's not elegant:
List<string> str = new List<string>();
while (!file.EndOfStream)
{
var line = file.ReadLine();
str.Add(line);
}
file.Close();
using (var sw = new StreamWriter(file))
{
for(int i = 0; i <= str.Count-1; i++)
{
var values = str[i].Split(',');
if (values[0] == "A" && values[1] != "2")
{
sw.WriteLine(str[i]);
int j = i+1;
for (int e = j; e <= str.Count - 1; e++)
{
var values2 = str[e].Split(',');
if (values2[0] == "B")
{
sw.WriteLine(str[e]);
}else if(values2[0] == "A")
{
break;
}
}
}
}
}
I would probably do something like this. Note that this assumes that the file always looks like your example and does no error checking:
using (StreamReader reader = new StreamReader(inputFile))
using (StreamWriter writer = new StreamWriter(outputFile))
{
bool delete = false;
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
string[] lineItems = line.Split(',');
if (lineItems[0].Trim() == "A")
delete = lineItems[1].Trim() == "2";
if (!delete)
writer.WriteLine(line);
}
}
Since you already know from the current A that you will remove it and any B's following it, you could simply retain the state in a boolean.
static void ProcessStream(TextReader input, TextWriter output)
{
bool remove = false;
string line;
while ((line = input.ReadLine()) != null)
{
var parts = line.Split(',');
//for A, decide to remove this and next lines
if (parts[0] == "A")
remove = parts[1].Contains("2");
if (!remove)
output.WriteLine(line);
}
}

How many values are in sequence from an array

I want to find how many values, in an array, are in sequence without sorting.
For Instance, if I have.
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 9 } //0, 1, 2, 3, 4, 5, 6 are in sequence
int value = HighestSequence(arr);
//value prints 7
int[] arr = new int[] { 0, 4, 1, 2, 3, 4, 7, 9 } //1, 2, 3, 4 are in sequence
int value = HighestSequence(arr);
//value prints 4
int[] arr = new int[] { 0, 1, 2 } //0, 1, 2 is in sequence
int value = HighestSequence(arr);
//value prints 3
You don't specify what should happen if there is more than one subsequence in order, but I've assumed there will be only one.
Try this:
int length = 1;
int[] arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 9 }; //0, 1, 2, 3, 4, 5, 6 are in sequence
//value prints 7
List<int> temp = arr.Zip(arr.Skip(1), (i1, i2) => length += (i2 - i1 == 1 ? 1 : 0)).ToList();
Length will contain the number of integers in sequence where arr[n] == arr[n+1] - 1
Cheers
EDIT:
In the case where there is more than one subsequence that is ordered such that arr[n] == arr[n+1] - 1, we'd need to decide how to handle it.
One way would be to reset the length when we find a value that doesn't meet the criteria:
arr = new int[] { 0, 1, 2, 5, 4, 5, 6, 9 }; //Possible bug ?
length = 1;
temp = arr.Zip(arr.Skip(1), (i1, i2) =>
{
if(i2 - i1 == 1)
{
length++;
}
else
{
length = 1;
}
return i1;
}).ToList();
But then this will not consider the "longest" subsequence, it will return the length of the "last" subsequence in the sequence.
The OP should specify what action he wants in such cases.
EDIT #2:
If we want to have the longest subsequence, then this could be used:
arr = new int[] { 0, 1, 2, 3, 4, 5, 6, 7, 5, 4, 5, 6, 7, 9 }; //Possible bug ?
length = 1;
int longest = length;
temp = arr.Zip(arr.Skip(1), (i1, i2) =>
{
if (i2 - i1 == 1)
{
if (++length > longest)
longest = length;
}
else
{
length = 1;
}
return i1;
}).ToList();
Caching the longest ordered subsequence length. Then use longest instead of length as the result.
EDIT #3:
Edits #1 & 2 should now contain the appropriate solution. I was obviously trying to come up with a solution to a comment from a user too quickly and I didn't realize that the current code in my VS solution was different than the code I posted here.
It is to be mentioned that the OP didn't have those additional constraints, and that I did mention right from the get go that my solution didn't address those additional constraints.
Nonetheless, it was an interesting problem to solve ;-)
Cheers
Try this out. I got the results of (7,4,3). Although Peter is correct about StackOverflow you should give your attempt and say your issue not just ask for an answer. I only provided it because it was a neat challenge.
var set1 = new [] { 0, 1, 2, 3, 4, 5, 6, 9 };
var result1 = HighestSequence(set1);
var set2 = new[] { 0, 4, 1, 2, 3, 4, 7, 9 };
var result2 = HighestSequence(set2);
var set3 = new [] { 0, 1, 2 };
var result3 = HighestSequence(set3);
public int HighestSequence(int[] values)
{
IList<int> sequenceCounts = new List<int>();
var currentSequence = 0;
for (var i = 0; i < values.Length; i++)
{
if (i == (values.Length - 1)) //End edge case
{
if (values[i] - values[i - 1] == 1)
{
currentSequence++;
sequenceCounts.Add(currentSequence);
}
}
else if ((values[i] + 1) == values[i + 1])
{
currentSequence++;
}
else
{
currentSequence++;
sequenceCounts.Add(currentSequence);
currentSequence = 0;
continue;
}
sequenceCounts.Add(currentSequence);
}
return sequenceCounts.Max();
}

How can I parse this using JSON.Net?

I'm trying to use JSON.Net to parse the results returned from a third party API.
As you can see the first block seems to be a description for the rows block columns that follow. I'm assuming this isn't standard practice as I cant find any reference to this style anywhere.
As it's not in the usual name:value pair format I'm a bit stumped.
{ cols: [{label: "name", type: 'string'},
{label: "caller_id_number", type: 'string'},
{label: "destination_number", type: 'string'},
{label: "call_start", type: 'datetime'},
{label: "duration", type: 'number'},
{label: "bill_seconds", type: 'number'},
{label: "uuid", type: 'string'},
{label: "call_bill_total", type: 'number'},
{label: "recorded", type: 'boolean'}],
rows: [
{c:[{v: "mydomain.com"},
{v: "1650"},
{v: "01902321654"},
{v: new Date(2011, 6, 19, 14, 12, 25)},
{v: 3},
{v: 0},
{v: "07752f6c-b203-11e0-92e6-495a2db86d6d"},
{v: 0},
{v: true}]}
,{c:[{v: "mydomain.com"},{v: "1652"},{v: "034534514"},{v: new Date(2011, 6, 19, 14, 11, 34)},{v: 53},{v: 27},{v: "e8fe3a06-b202-11e0-92dd-495a2db86d6d"},{v: 0.05},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1650"},{v: "034534580"},{v: new Date(2011, 6, 19, 14, 11, 34)},{v: 11},{v: 9},{v: "e8dfb9dc-b202-11e0-92dc-495a2db86d6d"},{v: 0.02},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1650"},{v: "03453453600"},{v: new Date(2011, 6, 19, 14, 11, 11)},{v: 14},{v: 9},{v: "db7efd52-b202-11e0-92d6-495a2db86d6d"},{v: 0.02},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1650"},{v: "0345345947"},{v: new Date(2011, 6, 19, 14, 9, 41)},{v: 42},{v: 21},{v: "a59314bc-b202-11e0-92c7-495a2db86d6d"},{v: 0.04},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1653"},{v: "345345420"},{v: new Date(2011, 6, 19, 14, 9, 41)},{v: 28},{v: 0},{v: "a5a953f8-b202-11e0-92c8-495a2db86d6d"},{v: 0},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1650"},{v: "353453120"},{v: new Date(2011, 6, 19, 14, 8, 52)},{v: 28},{v: 5},{v: "885515bc-b202-11e0-92bd-495a2db86d6d"},{v: 0.02},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1653"},{v: "34534567"},{v: new Date(2011, 6, 19, 14, 8, 36)},{v: 10},{v: 3},{v: "7efc86d0-b202-11e0-92b8-495a2db86d6d"},{v: 0.02},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1650"},{v: "34534584"},{v: new Date(2011, 6, 19, 14, 7, 43)},{v: 34},{v: 13},{v: "5f1cfb60-b202-11e0-92b2-495a2db86d6d"},{v: 0.02},{v: true}]},
{c:[{v: "mydomain.com"},{v: "1653"},{v: "34534534561"},{v: new Date(2011, 6, 19, 14, 6, 52)},{v: 52},{v: 0},{v: "411b3faa-b202-11e0-92ab-495a2db86d6d"},{v: 0},{v: true}]}]}
I've only got as far as
var o = JObject.Parse(results);
var records = o.SelectToken("rows").Select(s => s).ToList();
Ideally I'd like to pull the records back into a class such as
public class CallDetailRecord
{
public String Name { get; set; }
public String CallerIdNumber { get; set; }
public String DestinationNumber { get; set; }
public DateTime CallStart { get; set; }
public int Duration { get; set; }
public String Uuid { get; set; }
public Decimal CallBillTotal { get; set; }
public bool Recorded { get; set; }
}
Many thanks for any help.
I don't know what that is, but it's not JSON. It looks like javascript and would likely parse fine with a javascript engine.
JSON spec: http://json.org/
Validator: http://jsonlint.com/
While your sample data is not strictly valid JSON, your attempt to parse it was pretty close.
The layout that you're seeing is sometimes used by some parties who believe that the size of their result sets could be improved (decreased) by aliasing the field names. Unfortunately it isn't as straightforward to work with this, but you can pivot the items back into objects.
My preference in these cases is to use the dynamic keyword and ExpandoObjects. You can use a class if you like, as the bulk of the work of creating an object happens in the final Select() below and you can rewrite it to map the v element sets into fields of a class instead of an ExpandoObject. The syntax to access a field is the same, as you can see by the snippet at the end that writes all the values to the Console.
Note that I've written a helper lambda to handle the case of mapping Date() into DateTime(). I'm just pointing this out as you may have a better place to put this method (an extension method on DateTime, perhaps); but there's no harm in copying and pasting it as-is into a suitable place your code.
using System.Dynamic;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
// ... other code removed
// You already have a means that loads your pseudo-json into results
// I used a file for the sake of this example
string results = File.ReadAllText(#"C:\temp\sample.json");
var o = JObject.Parse(results);
var headers = o.SelectToken("cols")
.Select(x => { return new { label = x.SelectToken("label").Value<string>(), type = x.SelectToken("type").Value<string>()}; }).ToArray();
var rows = o.SelectToken("rows").Select(s => { return s.SelectToken("c");}).ToList();
Func<JConstructor, DateTime> MapAsDateTime = (s) =>
{
// This is sloppy on my part, you should improve this as you like.
List<int> v = new List<int>();
foreach (JToken t in s)
{
v.Add(t.Value<int>());
}
return new DateTime(v[0], v[1], v[2], v[3], v[4], v[5]);
};
IEnumerable<dynamic> finalValues = rows.Select(s =>
{
var innerValues = s.ToList().Select(x => { return x.SelectToken("v"); }).ToArray();
int i = 0;
dynamic val = new ExpandoObject();
IDictionary<string, object> valueMap = (IDictionary<string, object>)val;
foreach (var innerValue in innerValues)
{
switch (headers[i].type)
{
case "string":
// NOTE: This can be improved, you could try to match and convert GUIDs with a regex or something else.
valueMap[headers[i].label] = innerValue.Value<string>();
break;
case "datetime":
valueMap[headers[i].label] = MapAsDateTime((JConstructor)innerValue);
break;
case "number":
// NOTE: This can be improved, your specific case needs decimal to handle things like 0.25, but many others could get by with just int
valueMap[headers[i].label] = innerValue.Value<decimal>();
break;
case "boolean":
valueMap[headers[i].label] = innerValue.Value<bool>();
break;
default:
// NOTE: You will need to add more cases if they 'define' more types.
throw new ArgumentException(string.Format("unhandled type \"{0}\" found in schema headers.", headers[i].type));
}
i++;
}
return val;
});
foreach (dynamic d in finalValues)
{
Console.WriteLine("name: {0}", d.name);
Console.WriteLine("caller_id_number: {0}", d.caller_id_number);
Console.WriteLine("destination_number: {0}", d.destination_number);
Console.WriteLine("call_start: {0}", d.call_start);
Console.WriteLine("duration: {0}", d.duration);
Console.WriteLine("bill_seconds: {0}", d.bill_seconds);
Console.WriteLine("uuid: {0}", d.uuid);
Console.WriteLine("call_bill_total: {0}", d.call_bill_total);
Console.WriteLine("recorded: {0}", d.recorded);
Console.WriteLine("--");
}
And finally, the sample output for the very first unit of data in your sample.
name: mydomain.com
caller_id_number: 1650
destination_number: 01902321654
call_start: 6/19/2011 2:12:25 PM
duration: 3
bill_seconds: 0
uuid: 07752f6c-b203-11e0-92e6-495a2db86d6d
call_bill_total: 0
recorded: True
--

Categories

Resources